I want to create a String using a format, replacing some tokens in the format with properties from a bean. Is there a library that supports this or am I going to have to create my own implementation?
Let me demonstate with an example. Say I have a bean Person;
public class Person {
private String id;
private String name;
private String age;
//getters and setters
}
I want to be able to specify format strings something like;
"{name} is {age} years old."
"Person id {id} is called {name}."
and automatically populate the format placeholders with values from the bean, something like;
String format = "{name} is {age} old."
Person p = new Person(1, "Fred", "32 years");
String formatted = doFormat(format, person); //returns "Fred is 32 years old."
I've had a look at MessageFormat but this only seems to allow me to pass numeric indexes, not bean properties.
Rolled my own, testing now. Comments welcome.
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BeanFormatter<E> {
private Matcher matcher;
private static final Pattern pattern = Pattern.compile("\\{(.+?)\\}");
public BeanFormatter(String formatString) {
this.matcher = pattern.matcher(formatString);
}
public String format(E bean) throws Exception {
StringBuffer buffer = new StringBuffer();
try {
matcher.reset();
while (matcher.find()) {
String token = matcher.group(1);
String value = getProperty(bean, token);
matcher.appendReplacement(buffer, value);
}
matcher.appendTail(buffer);
} catch (Exception ex) {
throw new Exception("Error formatting bean " + bean.getClass() + " with format " + matcher.pattern().toString(), ex);
}
return buffer.toString();
}
private String getProperty(E bean, String token) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field field = bean.getClass().getDeclaredField(token);
field.setAccessible(true);
return String.valueOf(field.get(bean));
}
public static void main(String[] args) throws Exception {
String format = "{name} is {age} old.";
Person p = new Person("Fred", "32 years", 1);
BeanFormatter<Person> bf = new BeanFormatter<Person>(format);
String s = bf.format(p);
System.out.println(s);
}
}
Yes, it's possible using the Pojomatic library. Implement and plug in your own implementation of PojoFormatter. Pojomator#doToString(T) may be also interesting.
Don't really know how complex is the model you're up to consume but if you want to deal with object trees I would implement my own formatter using Jexl as expession language this way:
Initialize a singleton Jexl engine
Populate a MapContext with all the objects you want to consume when formatting strings
Parse your strings and create a Jexl expression per "${}" construct you have.
Evaluate the previous created expressions against the object context map.
The good thing about Jexl is that it will allow you to use method calls, not just properties.
Hope it helps.
Not quite close, but you can look at StringTemplate, your bean:
public static class User {
public int id; // template can directly access via u.id
private String name; // template can't access this
public User(int id, String name) { this.id = id; this.name = name; }
public boolean isManager() { return true; } // u.manager
public boolean hasParkingSpot() { return true; } // u.parkingSpot
public String getName() { return name; } // u.name
public String toString() { return id+":"+name; } // u
}
Then you can render it like this:
ST st = new ST("<b>$u.id$</b>: $u.name$", '$', '$');
st.add("u", new User(999, "parrt"));
String result = st.render(); // "<b>999</b>: parrt"
Code sample above taken from ST4 Introduction
Related
How to print any class instance in Java? Similar to JSON.stringify() in Javascript. Not necessary JSON, any format of output will do.
public class User {
public String name, password;
public int age;
public ArrayList<String> phones;
public static void login() {
//do something
}
}
User X = new User;
X.name = "john connor";
X.password = "skynet";
X.age = "33";
X.phones.add("1234567");
X.phones.add("7654321");
System.out.println(printClass(X))
Should output something like:
{ name:"john connor", password: "skynet", age: "33", phones:
["1234567", "7654321"], login: void function() }
You can use Apache's commons-lang's ToStringBuilder.reflectionToString
Of course, reflection is slow, so only do this with your test code. for normal use, please use eclipse's "Source" menu -> generate toString() (or intellij's generate toString()) which gives you a decent string.
There could be many ways to achieve what you need. Though i would be interested in why you need.
Override the toString() method.
see: http://www.javapractices.com/topic/TopicAction.do?Id=55
If the generation algorithm gets too long, then consider a separate class say UserPrettyPrinter.
public interface UserPrettyPrinter {
string print(User);
}
public class PrintUserInJSON implements UserPrettyPrinter {
string print(User user) {
//implement the algo here
}
}
you can also implement:
public class PrintUserInXML implements UserPrettyPrinter {
string print(User user) {
//implement the algo here
}
}
Either in conjugation to number-2 or as a standalone class, you can write
public class PrintObjectBasicAlgo {
String print(Object obj) {
/* i write pseudo code here. just ask if you cannot implement this
this would help: http://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html
Class class = Obj.getClass();
Filed[] allVariables = class.getAllFieldsByReflection();
ArrayList<String> keys = new ArrayList<String>;
ArrayList<String> values = new ArrayList<String>;
for(Field field : allVariables) {
Object value = reflectionGetValueOfField( field, obj );
keys.add( field.getName());
values.add(value.toString());
}
now that you have the keys and values, you can generate a string in anyway you like
*/
}
}
You may see Visitor Pattern. it might be helpful.
You have two options here. The simple one is just to override the toString function for your class. I dont see why you dont do this really. In this case its as simple as
String toString(){
return "{ name:\""+name+", password: \""+passowrd....
}
The second option is to use reflection. This would be slightly (though not really) better if you had some sorta external class used for "printing classes". The pseudo code for that would be
StringBuilder s = new StringBuidler();
for(Field f : fields){
s.append(f.getName() + "\" :\"" + f.get()+ "\"");
}
return s.toString();
However this would be costly as reflection always is. Also if you just properly override the toString functions in the first place your printClass function could literally just be
String printClass(Object o){ return o.toString();}
Which of course again begs the question of why do you need a printClass function?
One option is to use Google Gson.
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
class Project {
private int year = 1987;
private String name = "ROBOCOP-1";
private boolean active = false;
private List<String> list = new ArrayList<String>() {
{
add("PROTECT THE INNOCENT");
add("UPHOLD THE LAW");
add("SERVE THE PUBLIC TRUST");
add("CLASSIFIED");
}
};
}
public class GsonExample {
public static void main(String[] args) {
Project obj = new Project();
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println(gson.toJson(obj));
}
}
This question already has answers here:
Converting many 'if else' statements to a cleaner approach [duplicate]
(7 answers)
Closed 4 years ago.
I think this is a very common situation in web projects. Assume there is an entity such as:
//JAVA code
#Data
class Entity{
private String a;
private String aExt;
private String b;
private String bExt;
private String c;
private String cExt;
... something more ...
}
For some purpose, I need to get part of values from Entity according to a passed argument, like:
public ViewObject foo(Entity entity, String condition){
ViewObject vo = new ViewObject();
if("aRelated".equals(condition)){
vo.setValue1(entity.getA());
vo.setValue2(entity.getAExt());
}
else if("bRelated".equals(condition)){
vo.setValue1(entity.getB());
vo.setValue2(entity.getBExt());
}
else if(cRelated".equals(condition)){
vo.setValue1(entity.getC());
vo.setValue2(entity.getCExt());
}
... else statement if there are other values ....
return vo;
}
I know I can use switch-case statement to reduce some words in foo(), but there is no essential difference compared with if-else, especially when the Entity has many variables.
As a plain Example, foo() is only a view object builder, but my project is more complex which have many duplicated code with only different variable's name in each if-else statement.
How do I reduce the above duplicated code?
You can try creating two hash maps:
// name these properly!
HashMap<String, Function<Entity, String>> valueMap = new HashMap<>();
HashMap<String, Function<Entity, String>> extMap = new HashMap<>();
Add these KVPs:
// valueMap
"aRelated" - Entity::getA
"bRelated" - Entity::getB
"cRelated" - Entity::getC
// extMap
"aRelated" - Entity::getAExt
"bRelated" - Entity::getBExt
"cRelated" - Entity::getCExt
Now, you can do this without an if statement:
vo.setValue1(valueMap.get(condition).apply(entity));
vo.setValue2(extMap.get(condition).apply(entity));
Another option would be to use reflection:
import java.lang.reflect.Method;
import java.lang.reflext.InvocationTargetException;
...
public ViewObject foo(Entity e, String c) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
String[] methodNames = { "get" + c.substring(0,1).toUpperCase(), "get" + c.substring(0,1).toUpperCase() + "Ext" };
Method[] methods = { e.getClass().getDeclaredMethod(methodNames[0]), e.getClass().getDeclaredMethod(methodNames[1]) };
ViewObject vo = new ViewObject();
vo.setValue1((String)methods[0].invoke(e));
vo.setValue2((String)methods[1].invoke(e));
return vo;
}
Although I have to admit I personally like the map approach of the other answers more, just showing more options.
Use of a Map would do the trick:
class EntityPart {
String s;
String sExt;
}
class Entity {
Map<String,EntityPart> m = new HashMap<>();
m.add("aRelated",new EntityPart());
m.add("bRelated",new EntityPart());
....
}
public ViewObject foo(Entity entity, String condition) {
ViewObject vo = new ViewObject();
EntityPart ep = entity.m.get(condition);
vo.setValue1(ep.s);
vo.setValue2(ep.sExt);
return vo;
}
Make Entity as enum instead of class.
public enum Entity {
A("a", "aExt"), B("b", "bExt"), C("c", "cExt");
private final String name;
private final String text;
private Entity(String name, String text) {
this.name = name;
this.text = text;
}
public String getName() {
return name;
}
public String getText() {
return text;
}
public static Entity fromString(String raw) {
return LOOKUP.get(raw);
}
private static final Map<String, Entity> LOOKUP = new HashMap<>();
static {
for (Entity e : values()) {
LOOKUP.put(e.getName(), e);
}
}
}
And modify your foo method as
public ViewObject foo(String condition){
/*
* pass condition as "a", "b", "c" only not "aRelated", "bRelated", "cRelated"
*
*/
ViewObject vo = new ViewObject();
Entity e = Entity.fromString(condition);
if(null != e) {
vo.setValue1(e.getName());
vo.setValue2(e.getText());
}
return vo;
}
I have one scenario , please have a look
Sentence can be :
String Subject + String Verb + String Object
String Subject + String Verb + String Adverb+ String Object
String Subject + String Verb + String Adjective+ String Object
String Subject + String Verb
I want to create a Sentence class object.
Sentence s = new Sentence();
s.setSubject();
...
Now , I can keep all fields in Sentence class, but I want to ensure that no field must be null.
I want to create any type of sentence from fields that I initialize where i create object.
eg:
Sentence s = new Sentence();
s.setSubject();
s.setVerb();
s.setObject();
s.getValue(); // will return sentence value as S+V+O as I have set SVO
Sentence s = new Sentence();
s.setSubject();
s.setVerb();
s.getValue(); // will return sentence value as S+V as I have set SV
and so on ... can I do something like this in java.
Basically, I want to avoid uninitialized fields and nullpointers.
EDIT
Please note that if i leave fields empty ie "" or null , sentences formed will be like
"I am null to school." or (verb is null) .
"I am to school" (verb is empty) .
So, please consider this scenario before answering.
I will prefer using a builder pattern, make the usage clearer for the end user and avoid mistakes
Public class Sentence {
private String subject;
private String verb;
private String adjective;
private String object;
//package private to ensure no one can call it outside package, canbe made pvt as well
Sentence(String subject, String verb, String adjective, String object) {
this.subject = subject;
this.verb = verb;
this.adjective = adjective;
this.object = object;
}
public static class SentenceBuilder{
private String subject;
private String verb;
private String adjective;
private String object;
private static final String EMPTY = "";
private String sanitizeInput(String input){
if (input==null){
return EMPTY;
}
return input;
}
private String validateInput(String input){
if(input==null || input.isEmpty()){
throw new IllegalArgumentException("cant be null or empty");
}
return input;
}
public SentenceBuilder(String subject, String verb) {
this.subject = validateInput(subject);
this.verb = validateInput(verb);
}
public SentenceBuilder adjective(String adjective){
this.adjective = sanitizeInput(adjective);
return this;
}
public SentenceBuilder object(String object){
this.object = sanitizeInput(object);
return this;
}
public Sentence build(){
return new Sentence(subject,verb,adjective,object);
}
}
public static void main(String[] args) {
//sample usage
Sentence sentence = new SentenceBuilder("subject","verb")
.adjective("adjective")
.object("object")
.build();
}
}
Based on your comment, you don't want malformed sentences. That is, a sentence without a subject and a verb.
So, you can modify your constructor to get subject and verb and throw IllegalArgumentException if either of them is null or empty.
public Sentence(String subject, String verb) {
if (subject == null || subject.isEmpty()) {
throw new IllegalArgumentException("No Subject?");
}
// Same as above for verb.
this.subject = subject;
this.verb = verb;
}
This way, an object cannot be created without a subject or a verb.
In Java, there is no convenient way can do this, You need it's Option, use getOrElse to get the default value.In Scala, it has Option class do this thing.
I think you can use HashMap in Java to do this thing.
public class Test {
HashMap<String, String> data = new HashMap<>();
public void setSubject(String subject) {
data.put("subject", subject);
}
public void getSubject() {
data.getOrDefault("subject", "your_default_value");
}
public void setVerb(String verb) {
data.put("verb", verb);
}
public void getVerb() {
data.getOrDefault("verb", "your_default_value");
}
public void setObject(String object) {
data.put("object", object);
}
public void getObject() {
data.getOrDefault("object", "your_default_value");
}
}
First check whether the fields are null, and create a sentence based on that information:
if (s.getSubject != null)
{
//do something
}
if (s.getVerb != null)
{
//do something
}
etc.
I have made a class named Entity, and have the following code:
Entity zombie1 = new Entity();
I get input 'zombie' from a scanner, and then concatenate a number, based on level on the end of that, leaving 'zombie1' as the string... I want to be able to use that string and call
zombie1.shoot("shotgun");
but I can't seem to find a solution. I'd just do a if statement but I want to be able to create as many zombies as I want and not have to put in more if statements every single time.
I've read articles using reflection and forString but that doesn't seem to be what i'm looking for.
Any help would be nice.
Possible solutions are to use a Map<String, Entity> to be able to store and retrieve entities based on specific Strings. If you have a limited number of sub-types of Entity such as Zombies, Vampires, Victims, etc, you could have a Map<String, List<Entity>>, allowing you to map a String to a specific type of entity and then get that type by number.
e.g.,
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Foo002 {
private static final String ZOMBIE = "zombie";
public static void main(String[] args) {
Map<String, List<Entity>> entityMap = new HashMap<String, List<Entity>>();
entityMap.put(ZOMBIE, new ArrayList<Entity>());
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "John"));
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "Fred"));
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "Bill"));
for (Entity entity : entityMap.get(ZOMBIE)) {
System.out.println(entity);
}
}
}
class Entity {
private String type;
private String name;
public Entity(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
#Override
public String toString() {
return type + ": " + name;
}
}
This is not your best bet. Your best bet is to have a Map;
// PLEASE LOOK INTO WHICH MAP WOULD BE BEST FOR YOUR CASE OVERALL
// HASHMAP IS JUST AN EXAMPLE.
Map<String, Entity> zombieHoard = new HashMap<String, Entity>;
String getZombieID( int id )
{
return String.format( "zombie%s", id );
}
String createZombie() {
String zid = getZombieID( Map.size() );
Map.put( zid, new Entity() );
return zid;
}
void sendForthTheHoard() {
createZombie();
createZombie();
String currentZombie = createZombie();
zombieHoard.get( currentZombie ).shoot( "blow-dryer" );
zombieHoard.get( getZombieID( 1 ) ).eatBrains();
}
Put your zombies in an ArrayList. Example:
ArrayList<Entity> zombies = new ArrayList<Entity>();
Entity zombie1 = new Entity();
zombies.add(zombie1);
Entity zombie2 = new Entity();
zombies.add(zombie2);
etc...
Then when it is time to call a certain zombie to the following:
zombies.get(1).shoot("shotgun");
If you are talking about dynamically invoking a method on an object, you can use Reflection to get the method object and invoke it (Note: I may have inadvertantly mixed up some C# syntax in this Java):
Entity zombie1 = new Entity();
Method shootMethod = Entity.class.getMethod("shoot", new Class[] { string.class });
shootMethod.invoke(zombie1, new Object[] { "shotgun" });
Here is the basic code i'm trying to make work:
Field fields[] = SalesLetter.class.getDeclaredFields();
String fieldName;
for (int j = 0, m = fields.length; j < m; j++) {
fieldName = fields[j].getName(); //example fieldname [[headline]]
templateHTML = templateHTML.replace(fieldName, Letter.fieldName());
}
I believe I'm going about it wrong by trying to getDeclaredFields (which isn't even syntactically correct). When I finished my title, it came up with a few other stackoverflow questions which I read before writing this. They were:
Best way to replace tokens in a large text template
Replacing tokens in a string from an array
It gave me the idea of reading all legal [[tokens]] from a text file, putting them into a hash (err I mean map, this is java :D), then creating an object reference with the same name as that token.
I can't figure out how I would do such a thing in java specifically, or if that would work. Please assist.
Thanks in advance,
Cody Goodman
Note: I'm trying to make everything as flexible as possible, so maybe in the future I could add things such as "[[tokenname]]:this is token name, you need to really think about what the customer wants to come up with a good token name" in a text file, then those fields are generated on my form, and everything works :)
In order to read values from non-static fields of a type, you'll need a reference to an instance of the type:
public class ReflectFields {
static class Letter {
public int baz = 100;
}
static class SalesLetter extends Letter {
public String foo = "bar";
}
public static void main(String[] args) throws Exception {
// TODO: better exception handling, etc.
SalesLetter instance = new SalesLetter();
for (Field field : instance.getClass().getFields()) {
System.out.format("%s = %s%n", field.getName(), field.get(instance));
}
}
}
You'll also have to watch for private fields, etc. In general, this approach should be avoided as it breaks encapsulation by looking at class internals.
Consider using the JavaBean API.
public class BeanHelper {
private final Object bean;
private final Map<String, Method> getters = new TreeMap<String, Method>();
public BeanHelper(Object bean) {
this.bean = bean;
for (PropertyDescriptor pd : Introspector.getBeanInfo(bean.getClass(),
Object.class).getPropertyDescriptors()) {
getters.put(pd.getName(), pd.getReadMethod());
}
}
public Set<String> getProperties() { return getters.keySet(); }
public Object get(String propertyName) {
return getters.get(propertyName).invoke(bean);
}
public static void main(String[] args) {
BeanHelper helper = new BeanHelper(new MyBean());
for (String prop : helper.getProperties()) {
System.out.format("%s = %s%n", prop, helper.get(prop));
}
}
public static class MyBean {
private final String foo = "bar";
private final boolean baz = true;
public String getFoo() { return foo; }
public boolean isBaz() { return baz; }
}
}
Exception handling has been omitted for brevity, so you'll need to add some try/catch blocks (I suggest wrapping the caught exceptions in IllegalStateExceptions).
What about using a template engine like Freemarker, Velocity or StringTemplate:
replace [[ by ${ and ]] by }
create a model from a properties file containing the replacements
process templateHTML
Here an example with Freemarker (without Exception handling)
Configuration config = new Configuration();
StringTemplateLoader loader = new StringTemplateLoader();
config.setTeplateLoader(loader);
Map model = Properites.load(new FileInputStream("tokens.properties"));
loader.putTemplate("html.ftl", templateHTML);
Template template = config.getTemplate("html.ftl");
Writer out = new StringWriter();
template.process(root, out);
String result = out.toString();
StringTemplate may be more simple (replace [[ and ]] by $), but I am not fimilar with it:
Map model = Properites.load(new FileInputStream("tokens.properties"));
StringTemplate template = new StringTemplate(templateHTML);
template.setAttributes(model);
String result = template.toString();
The tokens.properties file looks like:
tokenname:this is token name