In Php I really often use this one:
$conn_sets = array();
$conn_sets['login'] = "aaa";
$conn_sets['pass'] = "bbb";
How to do the same in JAVA 1.6.
I tried to do this:
private method1() {
String[] mystring = new String[] {"login" => "aaa", "pass" => "bbb"};
}
But it give's me an error.
I want to make this work, because I have an error lists declarations, and it is better to identify:
throw new MyException(myerrors['failed_login_error']);
than a:
throw new MyException(myerrors[116]);
I know I can do a new class, and throw an object:
throw new MyException(ERROR_CONSTANTS.FAILED_LOGIN_ERROR);
But I prefer the first one (the same as I use in Php).
So, any ideas?
In Java, you probably want to use the Map interface, such as a HashMap.
Although, I would say that using an Enum is actually what you should be doing in your second (list of errors) example. Forget about PHP when you're in Java. Enums are much better in this case because you want a well-defined list of keys.
You really should be using Properties (or better yet a ResourceBundle to abstract the properties file) for this particular case.
Here is a tutorial on the usage.
This is a much better way as you can internationalize (I18N) the messages (if you want) and you can specify them in text files rather than inside the code (messages are much better in text than in code so you can update them without having to rebuild).
You don't want to use a HashMap or a Properties object to store wildly named parameters. You really want to think object-oriented and use a class to encapsulate the data of an account and to express what an object really represents in the real world:
String username = "aaa";
String password = "bbb";
Account acc = new Account(username,password);
if (!tryLogin(acc)) {
throw new LoginFailedException(account);
}
That way, clients who catch the LoginFailedException can make use of the information and use statically typed methods with good names to, for example, retrieve the username by calling loginFailedException.getUsername().
You could use the double brace pattern:
Map<String, String> map = new HashMap<String, String>() {{
put( "login", "aaa" );
put( "pass", "bbb" );
}};
... and your other example:
throw new MyException( myErrors.get( "failed_login_error" ) );
Related
Trying to implement Contract Testing using Pact.
I'm starting off with Consumer side right now. It is event-driven messages so I am using MessagePactBuilder
I will give an example of what I have and what I'm trying to achieve.
What I currently have:
#Pact
public MessagePact validMessage(MessagePactBuilder builder){
PactDslJsonBody body = new PactDslJsonBody();
body.object("student")
.stringType("studentFirstName")
.stringType("studentLastName")
.stringType("studentAddress") // I understand this won't work.
Map<String, String> metadata = new HashMap<String, String>();
metadata.put("contentType", "application/json");
return builder
.given("validMessage")
.expectsToReceive()
.withMetadata(metadata)
.withContent(body)
.toPact();
}
The issue I have is the student class is composed of
String studentFirstName
String studentLastName
Address studentAddress
So you can see that it is also taking in an Address object.
The Address object consists of all strings
String addressLine
String city
String state
String zip
Any ideas on how I can create the PactDslJsonBody this way? Or if I need to implement it another way? Any ideas would be appreciated.
You can find some examples at https://docs.pact.io/implementation_guides/jvm/consumer#examples
In your case, using the PactDslJsonBody, it would look like the following:
body
.object("student")
.stringType("studentFirstName")
.stringType("studentLastName")
.object("studentAddress")
.stringType("adressLine")
.stringType("city")
// Others...
.closeObject()
.closeObject()
(False indentation to match the JSON structure)
This can become very verbose and hard to read because of missing indentation. That's why there's an alternative syntax using LambdaDsl:
newJsonBody((o) -> {
o.object("student", (s) -> {
s.stringType("studentFirstName");
s.object("studentAddress", (a) -> {
a.stringType("adressLine");
// Others...
});
// Others...
});
})
.build();
This syntax naturally looks' like the JSON structure even though it's still quite verbose.
If you want less verbose syntax, only alternative is to use Kotlin or Scala.
I'm new to Java (and not too comfortable with strong typing) and I have a method that takes in a HashMap. A key in this hashmap contains a key, which has a hashmap for value, which also points to a hashmap, etc, until we reach a string:y
HashMap1->HashMap2->HashMap3->HashMap4->String
I am trying to access it as follows:
HashMap1
.get("aKey")
.get("anotherKey")
.get("yetAnotherKey")
.get("MyString");
But then I get an error,
Object does not have a method "get(String)
Here is the method, simplified:
public HashMap<String, HashMap> getMyString(Map<String, HashMap> hashMap1) {
String myString = hashMap1
.get("aKey")
.get("anotherKey")
.get("yetAnotherKey")
.get("MyString");
// do something with myString.
return hashMap1;
}
How would someone properly define the method and the parameters to access nested elements easily?
Thank you,
Simple as that
HashMap1.get("aKey") -- > return hashMap2
.get("anotherKey") --> return hashMap3
.get("yetAnotherKey") --> return hashMap4
.get("MyString"); --> return String
There is something wrong with the adding part.
Now you have structure like below.
hashmap1 --> hashmap2 --> String
String myString = hashMap1.get("aKey").get("MyString");
That is how it should be.
You made too many .get calls. Probably the last one is not needed.
Can you just create class CompoundKey with arbitrary number of String fields and use it as a key? It would simplify your design.
To use it properly in Java you need to override hashCode and equals methods.
You should first of all use interfaces not implementation, therefore use Map (and not HashMap) where possible.
And second, you should repair your Generics and use all levels. Now the compiler can help you and possible show your error.
// i suppose you want to return a String, at least the method name tells it
public String getMyString(Map<String, Map<String, Map<String, Map<String, String>>>> hashMap1) {
String myString = hashMap1
.get("aKey")
.get("anotherKey")
.get("yetAnotherKey")
.get("MyString");
return myString;
}
Yet i suggest that you use a different data structure.
I have a class called Info, and i have a bunch of static String variables described in it.
public class Info{
public static stringOne= "Hello";
public static stringTwo = "world";
}
and i'm hoping to access these variables as Info.stringTwo from other classes.
1.) I need to know if this is java-Internationalization that i have applied here ? (I have all the messages that i will display in the application assigned in this class. And, i am hoping to have different languages support to the app as well)
Have a look at Resource bundle
A copy paste from the documentation:
When your program needs a locale-specific object, it loads the ResourceBundle class using the getBundle method:
ResourceBundle myResources =
ResourceBundle.getBundle("MyResources", currentLocale);
Resource bundles contain key/value pairs. The keys uniquely identify a locale-specific object in the bundle.
Here's an example of a ListResourceBundle that contains two key/value pairs:
public class MyResources extends ListResourceBundle {
protected Object[][] getContents() {
return new Object[][] {
// LOCALIZE THE SECOND STRING OF EACH ARRAY (e.g., "OK")
{"OkKey", "OK"},
{"CancelKey", "Cancel"},
// END OF MATERIAL TO LOCALIZE
};
}
}
Keys are always Strings. In this example, the keys are "OkKey" and "CancelKey". In the above example, the values are also Strings--"OK" and "Cancel"--but they don't have to be. The values can be any type of object.
You retrieve an object from resource bundle using the appropriate getter method. Because "OkKey" and "CancelKey" are both strings, you would use getString to retrieve them:
button1 = new Button(myResources.getString("OkKey"));
button2 = new Button(myResources.getString("CancelKey"));
Here is an example from here:-
import java.util.*;
public class InternationalizationDemo {
public static void main(String[] args) {
String language;
String country;
Locale locale;
ResourceBundle rb;
if (args.length != 2) {
language = new String("en");
country = new String("US");
}
else {
language = new String(args[0]);
country = new String(args[1]);
}
locale = new Locale(language, country);
rb = ResourceBundle.getBundle("MessagesBundle", locale);
System.out.println(rb.getString("localeInfo") + " ( " +
locale.getDisplayLanguage() + "," + locale.getDisplayCountry() + ").\n");
System.out.println(rb.getString("welcome"));
System.out.println(rb.getString("sayThanks"));
}
}
Though using a ResourceBundle is the traditional and most well-known approach to internationalization in Java, it is possible to make internationalization data available as class members, somewhat similar to the way you seek.
You can further put your strings for some message in different languages in a Map, indexed by language. And make this Map a static member of some class. Thus you get the ability to reference these string collections for messages by their class member names in a compiler-checked manner. And next, if you have a way to select preferred user language at run time (you have to have it), you just pick the right string from an appropriate collection using its language key, boiling down to something like this:
logger.info (MyClassWithMessages.MY_MULTILANGUAGE_MESSAGE.s ());
And the s() method to be added to your Map subclass can be made resposible for dealing with user preferences and selection from Map by language key.
That said, the remaining task is just to formulate a convenient API for all this... You are welcome to have a look at such an implementation on my blog page Look Ma, no ResourceBundle :) ..., and the next page that goes ahead with message formatting arguments.
For internationalization of Java and other applications I implemented a Message Compiler, which creates the resource bundle files and constant definitions as Java enum or static final strings for the keys from one single source file. So the constants can be used in the Java source code, which is a much safer way than using plain string constants. The message compiler cannot only be used for Java. It creates also resource files and constants for Objective-C or Swift and can be extended for other programming environments.
I have a system where I receive a string from an outside library, and need to select a specific class based on that string. Unfortunately, I have no control over the string I receive. Is there a more object-oriented way to do this in Java than
if(string.equals(CASE1)){
return new CaseOneObject();
} else if (string.equals(CASE2)){
return new CaseTwoObject();
} else if
...
Each string corresponds to a single class, but there isn't a reliable way to derive the class name from the string.
Or put the names or classes in a map and select them using the string as the key.
Map<String, Class<?>> classes = new HashMap<String, Class<?>>;
Class<?> aClass = com.github.my.overlong.package.SomeClass.class;
classes.put("silly_string_a", aClass);
/* .... */
You could setup a static mapping like this at the top of your class managing this:
static Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
static {
classMap.put("case1", CaseOneObject.class);
classMap.put("case2", CaseTwoObject.class);
}
And then lookup the string you get from the outside library to create an instance if it's available:
Class<?> clazz = classMap.get(string);
if (clazz != null) {
return clazz.newInstance();
}
you may want to store a map of your instances and use it when the string has sent
Map<String,Object> map = new HashMap<String,Object>();
map.put("SomeString",new MyObject());
map.put("SomeString2",new MyObject2());
or you can save the class instead of new MyObject2() and create new instance.
now when you want to get the data you go:
return map.get(myString);
As others have noted, there is the Map<String, Object> solution, which is quite nice; however if you really want to switch off of Strings, you cannot do it until Java 7 becomes available.
Project coin added several extensions to the Java language. One of those extensions was "switch statements based on Strings", an example of the supported String enabled switch statement follows.
SIMPLE EXAMPLE: Show the simplest possible program utilizing the new feature.
String s = ...
switch(s) {
case "foo":
processFoo(s);
break;
}
ADVANCED EXAMPLE: Show advanced usage(s) of the feature.
String s = ...
switch(s) {
case "quux":
processQuux(s);
// fall-through
case "foo":
case "bar":
processFooOrBar(s);
break;
case "baz":
processBaz(s);
// fall-through
default:
processDefault(s);
break;
}
Store the String to classname mapping in a map. Use reflection with proper exception handling to instantiate the class
Edit
map.put ("case1", "class1")
....
Class.forName(map.get("case1")).newInstance();
However, you will need to Type the returned object to desired one before being able to do something useful with it.
Is there any String replacement mechanism in Java, where I can pass objects with a text, and it replaces the string as it occurs?
For example, the text is:
Hello ${user.name},
Welcome to ${site.name}.
The objects I have are user and site. I want to replace the strings given inside ${} with its equivalent values from the objects. This is same as we replace objects in a velocity template.
Use StringSubstitutor from Apache Commons Text.
Dependency import
Import the Apache commons text dependency using maven as bellow:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
</dependency>
Example
Map<String, String> valuesMap = new HashMap<String, String>();
valuesMap.put("animal", "quick brown fox");
valuesMap.put("target", "lazy dog");
String templateString = "The ${animal} jumped over the ${target}.";
StringSubstitutor sub = new StringSubstitutor(valuesMap);
String resolvedString = sub.replace(templateString);
Take a look at the java.text.MessageFormat class, MessageFormat takes a set of objects, formats them, then inserts the formatted strings into the pattern at the appropriate places.
Object[] params = new Object[]{"hello", "!"};
String msg = MessageFormat.format("{0} world {1}", params);
My preferred way is String.format() because its a oneliner and doesn't require third party libraries:
String message = String.format("Hello! My name is %s, I'm %s.", name, age);
I use this regularly, e.g. in exception messages like:
throw new Exception(String.format("Unable to login with email: %s", email));
Hint: You can put in as many variables as you like because format() uses Varargs
I threw together a small test implementation of this. The basic idea is to call format and pass in the format string, and a map of objects, and the names that they have locally.
The output of the following is:
My dog is named fido, and Jane Doe owns him.
public class StringFormatter {
private static final String fieldStart = "\\$\\{";
private static final String fieldEnd = "\\}";
private static final String regex = fieldStart + "([^}]+)" + fieldEnd;
private static final Pattern pattern = Pattern.compile(regex);
public static String format(String format, Map<String, Object> objects) {
Matcher m = pattern.matcher(format);
String result = format;
while (m.find()) {
String[] found = m.group(1).split("\\.");
Object o = objects.get(found[0]);
Field f = o.getClass().getField(found[1]);
String newVal = f.get(o).toString();
result = result.replaceFirst(regex, newVal);
}
return result;
}
static class Dog {
public String name;
public String owner;
public String gender;
}
public static void main(String[] args) {
Dog d = new Dog();
d.name = "fido";
d.owner = "Jane Doe";
d.gender = "him";
Map<String, Object> map = new HashMap<String, Object>();
map.put("d", d);
System.out.println(
StringFormatter.format(
"My dog is named ${d.name}, and ${d.owner} owns ${d.gender}.",
map));
}
}
Note: This doesn't compile due to unhandled exceptions. But it makes the code much easier to read.
Also, I don't like that you have to construct the map yourself in the code, but I don't know how to get the names of the local variables programatically. The best way to do it, is to remember to put the object in the map as soon as you create it.
The following example produces the results that you want from your example:
public static void main(String[] args) {
Map<String, Object> map = new HashMap<String, Object>();
Site site = new Site();
map.put("site", site);
site.name = "StackOverflow.com";
User user = new User();
map.put("user", user);
user.name = "jjnguy";
System.out.println(
format("Hello ${user.name},\n\tWelcome to ${site.name}. ", map));
}
I should also mention that I have no idea what Velocity is, so I hope this answer is relevant.
Here's an outline of how you could go about doing this. It should be relatively straightforward to implement it as actual code.
Create a map of all the objects that will be referenced in the template.
Use a regular expression to find variable references in the template and replace them with their values (see step 3). The Matcher class will come in handy for find-and-replace.
Split the variable name at the dot. user.name would become user and name. Look up user in your map to get the object and use reflection to obtain the value of name from the object. Assuming your objects have standard getters, you will look for a method getName and invoke it.
There are a couple of Expression Language implementations out there that does this for you, could be preferable to using your own implementation as or if your requirments grow, see for example JUEL and MVEL
I like and have successfully used MVEL in at least one project.
Also see the Stackflow post JSTL/JSP EL (Expression Language) in a non JSP (standalone) context
Handlebars.java might be a better option in terms of a Velocity-like syntax with other server-side templating features.
http://jknack.github.io/handlebars.java/
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hello {{this}}!");
System.out.println(template.apply("Handlebars.java"));
I use GroovyShell in java to parse template with Groovy GString:
Binding binding = new Binding();
GroovyShell gs = new GroovyShell(binding);
// this JSONObject can also be replaced by any Java Object
JSONObject obj = new JSONObject();
obj.put("key", "value");
binding.setProperty("obj", obj)
String str = "${obj.key}";
String exp = String.format("\"%s\".toString()", str);
String res = (String) gs.evaluate(exp);
// value
System.out.println(str);
I created this utility that uses vanilla Java. It combines two formats... {} and %s style from String.format.... into one method call. Please note it only replaces empty {} brackets, not {someWord}.
public class LogUtils {
public static String populate(String log, Object... objects) {
log = log.replaceAll("\\{\\}", "%s");
return String.format(log, objects);
}
public static void main(String[] args) {
System.out.println(populate("x = %s, y ={}", 5, 4));;
}
}
Since Java 15 you have the method String.formatted() (see documentation).
str.formatted(args) is the equivalent of String.format(str, args) with less ceremony.
For the example mentioned in the question, the method could be used as follows:
"Hello %s, Welcome to %s.".formatted(user.getName(), site.getName())
Good news. Java is most likely going to have string templates (probably from version 21).
See the string templates proposal (JEP 430) here.
It will be something along the lines of this:
String name = "John";
String info = STR."I am \{name}";
System.out.println(info); // I am John
P.S. Kotlin is 100% interoperable with Java. It supports cleaner string templates out of the box:
val name = "John"
val info = "I am $name"
println(info) // I am John
Combined with extension functions, you can achieve the same thing the Java template processors (e.g. STR) will do.
There is nothing out of the box that is comparable to velocity since velocity was written to solve exactly that problem. The closest thing you can try is looking into the Formatter
http://cupi2.uniandes.edu.co/site/images/recursos/javadoc/j2se/1.5.0/docs/api/java/util/Formatter.html
However the formatter as far as I know was created to provide C like formatting options in Java so it may not scratch exactly your itch but you are welcome to try :).