I have the following java code:
...
public final class Constants {
...
public static class Languages {
...
public static class en_US {
public static final String VALIDATION_REGEX = "[a-zA-Z-' ]+";
...
}
public static class en_GB {
public static final String VALIDATION_REGEX = "[a-zA-Z-' ]+";
...
}
}
...
}
My problem is as follows:
I receive a text and a language, and I have to check, whether that text is written only with valid alphabetic characters of that given language.
My code so far is as follows:
...
public boolean isContentValid(String content, String language) {
Boolean isCorrect = false;
switch (language) {
...
case "en_US":
isCorrect = content.matches(Constants.Phrases.en_US.VALIDATION_REGEX);
break;
case "en_GB":
isCorrect = content.matches(Constants.Phrases.en_GB.VALIDATION_REGEX);
break;
...
default:
isCorrect = false;
}
return isCorrect;
}
...
This is fine and works, but as I add languages to my application, I will have to add more and more cases to my switch.
And I was wondering if in Java there is a way to dynamically name a static nested class, something like:
Constants.Phrases[language].VALIDATION_REGEX
So my above code could be something like:
...
public boolean isContentValid(String content, String language) {
return content.matches(Constants.Phrases[language].VALIDATION_REGEX);
}
...
Thank you, and sorry if this is something super easy.
I am a JavaScript developer, and just learning Java.
Looking at you use case maybe this is a better approach:
public enum Language {
en_US("engUS_reg"),
en_GB("engGB_reg");
private final String regex;
Language(String regex) {
this.regex = regex;
}
public String getRegex() {
return regex;
}
}
And using this enum class write your method as follows:
public boolean isContentValid(String content, String language) {
return content.matches(Language.valueOf(language).getRegex());
}
You could use an enum for something like this.
"An enum can, just like a class, have attributes and methods. The only difference is that enum constants are public, static and final (unchangeable - cannot be overridden)." - [w3][1]
public enum Languages {
EN_US {
#Override
public String toString() {
return "[a-zA-Z-' ]+";
}
},
EN_GB {
#Override
public String toString() {
return "[a-zA-Z-' ]+";
}
},
}
And then you can access these values like this
Languages.valueOf("EN_US");
As mentioned by #Pshemo you could avoid a class based approach entirely and use an implementation of Map if you want something a little more lightweight
[1]: https://www.w3schools.com/java/java_enums.asp#:~:text=An%20enum%20can%2C%20just%20like,but%20it%20can%20implement%20interfaces).
Related
I have a config file with key value pair as
language = "IN"
and i have multiple page object enum files with name as
PageObject_US,PageObject_UK,PageObject_IN
every page object enum file has constants that can be accessed using for example
PageObjects_US.String.lable
but what i want to achieve is a way to create something like below
take the parameter from config file store it in a string
like String language = "IN"
Then concatenate using "PageObjects_" + language to get (PageObjects_IN)
so that the returned value can be used to fetch the constants from PageObjects_IN.String.label.
following is the code block:
if(!ENV.equalsIgnoreCase("development") && VALIDATION.equalsIgnoreCase("yes")) {
Elements.ByTitle(webDriver,PageObjects_IN.GREAT.label);
Elements.ByID(webDriver, PageObjects_IN.COUNTER.label);
}
In the above i want to use enum file PageObjects_IN at run time as i have many enum files
below is the enum
public enum PageObjects_IN {
// Text
GREAT("great"),
COUNTER("counter");
public final String lable;
PageObjects_IN(final String lable) {
this.lable = lable;
}
}
This is possible (using reflection) but strongly not recommended as it eliminates the efficiency of Java language constructs.
Not recommended way
Say you have a package click.webelement.cucumber.po where you store
public enum PO_EN {
GREAT("great_en"),
COUNTER("counter_en");
public final String label;
PO_EN(String label){
this.label = label;
}
}
and
public enum PO_IN {
GREAT("great_in"),
COUNTER("counter_in");
public final String label;
PO_IN(String label){
this.label = label;
}
}
Then to take a value you can do something like this:
String lang = "EN";
// Take class
Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
// Find an object that represent enum constant
Object val = Arrays
.stream(clazz.getEnumConstants()).filter(o -> "GREAT".equals(o.toString()))
.findAny()
.get();
// Take field value for that object
Field f = clazz.getField("label");
System.out.println(f.get(val));
This is error-prone approach and you would not have benefit from compile phase.
Recommended approach - 1
Instead of having enum use classes.
public abstract class PO {
public abstract String great();
public abstract String counter();
}
and
public class PO_EN extends PO{
#Override
public String great() {
return "great_en";
}
#Override
public String counter() {
return "counter_en";
}
}
and
public class PO_IN extends PO{
#Override
public String great() {
return "great_in";
}
#Override
public String counter() {
return "counter_in";
}
}
so your test would be much simpler
String lang = "EN";
Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
PO val = (PO) clazz.getDeclaredConstructor().newInstance();
System.out.println(val.great());
Recommended approach - 2
You can utilize PageFactory harness for your page objects and use this lib to parametrize your locators, like (if you use test ng):
#DataProvider(name = "languages")
Object[][] dataProvider(){
return new Object[][]{
{"en", "great_en", "counter_en"},
{"in", "great_in", "counter_in"}
};
}
#Test(dataProvider = "languages")
public void testPage(String language, String great, String counter){
DefaultParameterProvider
.properties
.set(Map.of("p.great", great, "p.counter", counter));
MyPage myPage = new MyPage(driver);
...
}
Where your page would be like this:
public class MyPage extends PageObjectParameterized {
#FindByParameterized(xpath = "//button[#name='{wec:p.great}']")
WebElement great;
#FindByParameterized(xpath = "//label[text()='{wec:p.counter}']")
WebElement counter;
#FindBy(xpath = "//input")
WebElement input;
public MyPage(SearchContext searchContext) {
super(searchContext);
}
}
Yes, I read many examples in web, but I didn't find a way how to call a method based on string value. May be I am not searching in right way... I wrote all code, but don't know how to call the method.
fyi: I don't want to use if else or switch case
Here is what I want:
I get the card reader type as String from database. I have to call the corresponding class' method.
My code:
LoginPanel.java
public class LoginPanel {
public static void main(String args[]) {
String readerType = "Omnikey5427-CK"; // I get this ("Omnikey5427-CK" or "Omnikey5427-G2") from a database as String
// I WANT TO CALL getCardNumber() method of respective class
}
}
ISmartCardReader.java
public interface ISmartCardReader {
public Integer getCardNumber();
}
Omnikey5427G2.java
public class Omnikey5427G2 implements ISmartCardReader {
public Omnikey5427G2() {
System.out.println("G222222222222222...");
}
public Integer getCardNumber() {
return 222;
}
}
Omnikey5427CK.java
public class Omnikey5427CK implements ISmartCardReader {
public Omnikey5427CK() {
System.out.println("CKKKKKKKKKKKKKKK...");
}
public Integer getCardNumber() {
return 111;
}
}
SmacrtCardEnumFactory.java
public enum SmacrtCardEnumFactory {
OMNIKEY5427CK("Omnikey5427-CK") {
public ISmartCardReader geInstance() {
return new Omnikey5427CK();
}
},
OMNIKEY5427G2("Omnikey5427-G2") {
public ISmartCardReader geInstance() {
return new Omnikey5427G2();
}
};
private String cardReaderName;
private SmacrtCardEnumFactory(String cardReaderName) {
this.cardReaderName = cardReaderName;
}
public String cardReaderName() {
return cardReaderName;
}
}
You can use valueOf() function of enum provided your enum sonstant names match strings used to lookup (you may use cardName.toUpper()). You may also create objects for all the card types and store them in a hash map and then lookup them. You can also write some fatory method, but this will be if-then-else or switch inside
You could iterate over the factory's values() and get the one that matches the string:
public enum SmacrtCardEnumFactory {
// current code omitted for brevity
public static getSmartCardReader(String name) {
return Arrays.stream(values())
.filter(r -> r.cardReaderName().equals(name))
.map(SmacrtCardEnumFactory::getInstance();
.orElse(null);
}
}
When I try to use enum to store: "=", ">", "<", etc, I have:
public static enum DataValueModifier {
EQUAL("="),
GREATER_THAN(">"),
GREATER_EUQAL(">="),
LESS_THAN("<"),
LESS_EQUAL("<="),
APPRROXIMATE("~"),
NOT_DETERMINED("ND");
private String value;
private DataValueModifier(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
How do I use it when I try to compare a string to see if it contains a "=" sign, should I do:
if (dataValue.contains(DataValueModifier.EQUAL.getValue())) {
...
}
I understand using enum is the better practice here, but this just looks silly...
Thanks,
David
If you defined a method boolean containedIn(String str) in your enum and imported your enum values of interest (in this case EQUAL), usage would look like:
if (EQUAL.containedIn(dataValue)) {
...
}
First of all, I'd move the "contains" method (or the equivalent of it) to the enum itself by defining an isModifier method.
public static enum DataValueModifier {
...
public boolean isModifier( String modifierString )
{
return modifierString != null && value.equals(modifierString);
}
}
Then, your code looks like this instead:
if (DataValueModifier.EQUAL.isModifier(dataValue))
{
//...
}
But, more importantly, why are you using dataValue instead of the enum in the first place? If you are getting command line input or something or parsing a string equation and then need to figure out the expression I guess I understand. But if you have control of the code then you should just start with the enum and you'll be able to say
if ( dataValueEnum == DataValueModifier.EQUAL ) {
{
//...
}
I'd also consider adding a static method to the enum that converts a given string to the correct enum value. It's not quite as efficient, perhaps, but unless you really care about efficiency it will make your code much cleaner. So add this method to your enum:
public static DataValueModifier toDataValueModifier( String dataValue ) {
if( EQUAL.isModifier( dataValue ) {
return EQUAL;
} else if( GREATER_THAN.isModifier( dataValue ) {
return GREATER_THAN;
} else if...
// Do this for all possible values
} else {
return UNKNOWN;
// Also, add an UNKNOWN to your list of enum values.
}
}
The isModifier and the toDataValueModifier methods might add a bit of ugly code to your DataValueModifier enum, but all your other code will look great. You can now do something like this:
DataValueModifier dataValueEnum = DataValueModifier.toDataValueModifier(dataValue);
if (dataValueEnum == DataValueModifier.EQUAL) {
...
}
or even
switch( DataValueModifier.toDataValueModifier(dataValue) ) {
case EQUAL:
// ...
break;
case GREATER_THAN:
// ...
break;
case GREATER_EQUAL:
// ...
break;
// ... define all the cases you want
case UNKNOWN:
default:
// ...
}
I like to use a static import in these cases.
package mypackage;
public enum DataValueModifier
{
//your enum code
}
then...
import static mypackage.DataValueModifier.*;
public MyClass
{
// code blah blah blah
public void doIt()
{
// more code blah blah
if (dataValue.contains(EQUAL.getValue()))
{
//...
}
}
}
It's a little nicer.
I am creating a helper class in parsing XML elements, so the developer do not need to know the exact name and capitalization of the XML fields.
private static class TagNames{
public static String RESOURCE_ID = "ResourceId";
public static String RESOURCE_NAME = "ResourceName";
public static String RESOURCE_PRICE = "ResourcePrice";
}
This makes it easier to do things like:
someXMLParser.getValueByTagName(TagNames.RESOURCE_ID);
My question is this. If I want to iterate over all the fields declared in class TagNames, how do I do that? Pseudocode:
For tag in TagNames:
someXMLParser.getValueByTagName(tag)
I know I will probably have to restructure all of this. But I can't figure out a way to make the names easily accessible as well as iterable, without any duplication.
Any suggestions?
You're literally asking for a solution based on reflection, but I think a Java Enum may be a better choice in this case. Building on Frederick's example:
public class EnumTest {
public enum Tags {
RESOURCE_ID("ResourceId"),
REOURCE_NAME("ResourceName"),
RESOURCE_PRICE("ResourcePrice");
private final String tagName;
Tags(String tagName) {
this.tagName = tagName;
}
public String getTagName() {
return tagName;
}
}
public static void main(String[] args) {
for(Tags tag : Tags.values()) {
System.out.println("const:" + tag.name()
+ " tagName:" + tag.getTagName());
}
// API user might do e.g.:
// document.getValueForTag(Tags.REOURCE_NAME);
}
}
Although I agree that you should probably use enums or ResourceBundles, here's a solution to your actual question. A method that generates a Map name -> value from all public constants in a given class (the only thing that's missing should be try / catch or throws)
public static Map<String, Object> getConstantValues(Class<?> clazz){
Map<String, Object> constantValues = new LinkedHashMap<String, Object>();
for(Field field : clazz.getDeclaredFields()){
int modifiers = field.getModifiers();
if(Modifiers.isPublic(mod)
&& Modifiers.isStatic(mod) && Modifiers.isFinal(mod)){
constantValues.put(field.getName(), field.get(null));
}
}
return constantValues;
}
You may want to consider using a ResourceBundle instead of a class to store the tag names. May require a little bit of reworking of your code but it will be easier to produce a list of tags compared to what you are doing now, and adding a new tag won't require much work other then adding a line to the properties file.
You can do this quite easily using enum and an accompanying array:
public class Main {
public enum TagName { RESOURCE_ID, REOURCE_NAME, RESOURCE_PRICE }
private static String[] tags = {"ResourceID", "ResourceName", "ResourcePrice"};
public static String getValueByTagName(TagName tag) {
return tags[tag.ordinal()];
}
public static void main(String[] args) {
System.out.println("Calling by getValueByTagName:");
System.out.println(getValueByTagName(TagName.RESOURCE_ID));
System.out.println("Calling TagName.values() for loop:");
for (TagName t : TagName.values()) {
System.out.println(getValueByTagName(t));
}
}
}
Using an enum is a good fit, especially if you use a custom constructor and the built in "values" method:
public class Main {
public static enum TagName {
RESOURCE_ID("ResourceId"),
RESOURCE_NAME("ResourceName"),
RESOURCE_PRICE("ResourcePrice"),
;
private String s;
private TagName(String s) { this.s = s; }
public String toString() { return this.s; }
public static String[] strings() {
List<String> ss = new ArrayList<String>();
for (TagName tagName : TagName.values()) {
ss.add(tagName.toString());
}
return ss.toArray(new String[ss.size()]);
}
}
public static void main(String[] args) {
// Use TagName.values() for the enums, or for strings...
for (String s : TagName.strings()) {
System.out.println(s);
}
}
}
This way you can simply add new tags and they'll automatically get picked up by the "strings" method; for extra performance you could compute that string array just once, statically, since you can't change the set of enums dynamically. You could get even fancier by auto-generating the tag strings from their constant values, if they are really normalized...
Since Java 1.4 doesn't have enums I'm am doing something like this:
public class SomeClass {
public static int SOME_VALUE_1 = 0;
public static int SOME_VALUE_2 = 1;
public static int SOME_VALUE_3 = 2;
public void receiveSomeValue(int someValue) {
// do something
}
}
The caller of receiveSomeValue should pass one those 3 values but he can pass any other int.
If it were an enum the caller could only pass one valid value.
Should receiveSomeValue throw an InvalidValueException?
What are good alternatives to Java 5 enums?
Best to use in pre 1.5 is the Typesafe Enum Pattern best described in the book Effective Java by Josh Bloch. However it has some limitations, especially when you are dealing with different classloaders, serialization and so on.
You can also have a look at the Apache Commons Lang project and espacially the enum class, like John has written. It is an implementation of this pattern and supports building your own enums.
I'd typically create what I call a constant class, some thing like this:
public class MyConstant
{
public static final MyConstant SOME_VALUE = new MyConstant(1);
public static final MyConstant SOME_OTHER_VALUE = new MyConstant(2);
...
private final int id;
private MyConstant(int id)
{
this.id = id;
}
public boolean equal(Object object)
{
...
}
public int hashCode()
{
...
}
}
where equals and hashCode are using the id.
Apache Commons Lang has an Enum class that works well and pretty well covers what Java 5 Enums offer.
If the application code base is going to use lot of enums, then I would prefer following solution, which I have used in my application.
Base Class
public class Enum {
protected int _enumValue;
protected Enum(int enumValue) {
this._enumValue = enumValue;
}
public int Value() {
return this._enumValue;
}
}
Your enumerations will then follow these pattern
Actual Enum
public class DATE_FORMAT extends Enum {
public static final int DDMMYYYY = 1;
public static final int MMDDYYYY = 2;
public static final int YYYYMMDD = 3;
public DATE_FORMAT(int enumValue) {
super(enumValue);
}
}
And your code can consume this enum as follows
String getFormattedDate(DATE_FORMAT format) {
String sDateFormatted = "";
switch (format.Value()) {
case DATE_FORMAT.DDMMYYYY :
break;
case DATE_FORMAT.MMDDYYYY :
break;
case DATE_FORMAT.YYYYMMDD :
break;
default:
break;
}
return sDateFormatted;
}
Caller can use the function as
void callerAPI() {
DATE_FORMAT format = new DATE_FORMAT(DATE_FORMAT.DDMMYYYY);
String sFormattedDate = getFormattedDate(format);
}
This is yet not full proof against intitializing derived Enum objects with any integer value. However it can provide good syntactic guideline to work in non-enum environment.