Json To Java Object having Class<?> member variable - java

public class A {
private Class<?> dataType;
public Class<?> getDataType() {
return dataType;
}
public void setDataType(Class<?> dataType) {
this.dataType = dataType;
}
}
public class B {
public static void main(String[] args) {
try {
File file = new File( "fileName.json");
A a = new ObjectMapper().readValue(file, A.class);
} catch (IOException io) {
io.printStackTrace();
}
}
}
Contents in fileName.json file :
{
"dataType" : "java.lang.String"
}
I got error : org.codehaus.jackson.map.JsonMappingException: Can not access private java.lang.Class() (from class java.lang.Class; failed to set access: Can not make a java.lang.Class constructor accessible.
Is there wrong representation of data in json file, if yes then can any one suggest me what will be correct notation of class A in json file.

I got the solution : We have to use Jackson annotation for this. So rewrite class A.
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
public class A {
private Class<?> dataType;
public Class<?> getDataType() {
return dataType;
}
public void setDataType(Class<?> dataType) {
this.dataType = dataType;
}
#JsonCreator
public A(#JsonProperty("dataType") String dataType)
{
try {
this.dataType = Class.forName(dataType);
} catch (ClassNotFoundException c) {
c.printStackTrace();
}
}
}

Related

Create an object by reflection

I'm trying to create an object using reflection like this
(maybe this is not a good architecture but I'm just doing some tests)
package com.interfaces;
public interface IEmployee {
List<entities.Employee> getEmployees();
}
then
package com.personnel;
public class Employee implements IEmployee{
public Employee(){}
public List<entities.Employee> getEmployees(){
...
}
}
then in another class
package com.factory;
import java.lang.reflect.Constructor;
public final class EmployeeFactory {
private static String path = "com.personnel";
public EmployeeFactory() {}
public static interfaces.IEmployee CreateEmployee(){
String className = path + ".Employee";
try {
Class<?> cls = Class.forName(className);
Object object = cls.newInstance();
return (interfaces.IEmployee)object;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
}
then in another class
public final class PersonnelService {
private static interfaces.IEmployee employeeFactory = com.factory.EmployeeFactory.CreateEmployee();
public List<entities.Employee> getEmployees(){
return employeeFactory.getEmployees();
}
}
finally in my main method
public static void main(String[] args) {
List<entities.Employee> employees = new com.services.PersonnelService().getEmployees();
for(entities.Employee employee : employees){
System.out.println(employee.getEmployeeName());
}
when the code arrives to Class cls = Class.forName(className) it throws the exception message "com.personnel.Employee.(java.lang.String)" with "NoSuchMethodException".
UPDATE
I found the problem. In fact, it was a bad naming convention. I had a serializable Employee class (my pojo) and an Employee class which implements my interface IEmployee which is intended to perform DB operations. I renamed this last one as EmployeeDAL and it worked. Even I defined the full class name as you can see in the example, it looks like that I was trying to instantiate my serializable pojo (I still don't get it, but...). Thanks for your time.
I tried to reproduce your code and not found any error on this code. I only get NoSuchMethodException message when I change Employee constructor with String argument
package com.agit.example.test;
import java.util.ArrayList;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List<Employee> employees = new PersonnelService().getEmployees();
for(Employee employee : employees){
System.out.println(employee);
}
}
}
interface IEmployee {
List<Employee> getEmployees();
}
class Employee implements IEmployee{
public Employee(){}
public List<Employee> getEmployees(){
List<Employee> x = new ArrayList<>();
x.add(this);
return x;
}
}
final class EmployeeFactory {
private static String path = "com.agit.example.test";
public EmployeeFactory() {}
public static IEmployee CreateEmployee(){
String className = path + ".Employee";
try {
Class<?> cls = Class.forName(className);
Object object = cls.newInstance();
return (IEmployee)object;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
}
final class PersonnelService {
private static IEmployee employeeFactory = EmployeeFactory.CreateEmployee();
public List<Employee> getEmployees(){
return employeeFactory.getEmployees();
}
}

Jackson and generic type [duplicate]

I want to use jackson json library for a generic method as follows:
public MyRequest<T> tester() {
TypeReference<MyWrapper<T>> typeRef = new TypeReference<MyWrapper<T>>();
MyWrapper<T> requestWrapper = (MyWrapper<T>) JsonConverter.fromJson(jsonRequest, typeRef);
return requestWrapper.getRequest();
}
public class MyWrapper<T> {
private MyRequest<T> request;
public MyRequest<T> getRequest() {
return request;
}
public void setRequest(MyRequest<T> request) {
this.request = request;
}
}
public class MyRequest<T> {
private List<T> myobjects;
public void setMyObjects(List<T> ets) {
this.myobjects = ets;
}
#NotNull
#JsonIgnore
public T getMyObject() {
return myobjects.get(0);
}
}
Now the problem is that when I call getMyObject() which is inside the request object Jackson returns the nested custom object as a LinkedHashMap. Is there any way in which I specify that T object needs to be returned? For example: if I sent object of type Customer then Customer should be returned from that List?
This is a well-known problem with Java type erasure: T is just a type variable, and you must indicate actual class, usually as Class argument. Without such information, best that can be done is to use bounds; and plain T is roughly same as 'T extends Object'. And Jackson will then bind JSON Objects as Maps.
In this case, tester method needs to have access to Class, and you can construct
JavaType type = mapper.getTypeFactory().
constructCollectionType(List.class, Foo.class)
and then
List<Foo> list = mapper.readValue(new File("input.json"), type);
'JavaType' works !!
I was trying to unmarshall (deserialize) a List in json String to ArrayList java Objects and was struggling to find a solution since days.
Below is the code that finally gave me solution.
Code:
JsonMarshallerUnmarshaller<T> {
T targetClass;
public ArrayList<T> unmarshal(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper.getDeserializationConfig()
.withAnnotationIntrospector(introspector);
mapper.getSerializationConfig()
.withAnnotationIntrospector(introspector);
JavaType type = mapper.getTypeFactory().
constructCollectionType(
ArrayList.class,
targetclass.getClass());
try {
Class c1 = this.targetclass.getClass();
Class c2 = this.targetclass1.getClass();
ArrayList<T> temp = (ArrayList<T>)
mapper.readValue(jsonString, type);
return temp ;
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null ;
}
}
I modified rushidesai1's answer to include a working example.
JsonMarshaller.java
import java.io.*;
import java.util.*;
public class JsonMarshaller<T> {
private static ClassLoader loader = JsonMarshaller.class.getClassLoader();
public static void main(String[] args) {
try {
JsonMarshallerUnmarshaller<Station> marshaller = new JsonMarshallerUnmarshaller<>(Station.class);
String jsonString = read(loader.getResourceAsStream("data.json"));
List<Station> stations = marshaller.unmarshal(jsonString);
stations.forEach(System.out::println);
System.out.println(marshaller.marshal(stations));
} catch (IOException e) {
e.printStackTrace();
}
}
#SuppressWarnings("resource")
public static String read(InputStream ios) {
return new Scanner(ios).useDelimiter("\\A").next(); // Read the entire file
}
}
Output
Station [id=123, title=my title, name=my name]
Station [id=456, title=my title 2, name=my name 2]
[{"id":123,"title":"my title","name":"my name"},{"id":456,"title":"my title 2","name":"my name 2"}]
JsonMarshallerUnmarshaller.java
import java.io.*;
import java.util.List;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
public class JsonMarshallerUnmarshaller<T> {
private ObjectMapper mapper;
private Class<T> targetClass;
public JsonMarshallerUnmarshaller(Class<T> targetClass) {
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper = new ObjectMapper();
mapper.getDeserializationConfig().with(introspector);
mapper.getSerializationConfig().with(introspector);
this.targetClass = targetClass;
}
public List<T> unmarshal(String jsonString) throws JsonParseException, JsonMappingException, IOException {
return parseList(jsonString, mapper, targetClass);
}
public String marshal(List<T> list) throws JsonProcessingException {
return mapper.writeValueAsString(list);
}
public static <E> List<E> parseList(String str, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(str, listType(mapper, clazz));
}
public static <E> List<E> parseList(InputStream is, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(is, listType(mapper, clazz));
}
public static <E> JavaType listType(ObjectMapper mapper, Class<E> clazz) {
return mapper.getTypeFactory().constructCollectionType(List.class, clazz);
}
}
Station.java
public class Station {
private long id;
private String title;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return String.format("Station [id=%s, title=%s, name=%s]", id, title, name);
}
}
data.json
[{
"id": 123,
"title": "my title",
"name": "my name"
}, {
"id": 456,
"title": "my title 2",
"name": "my name 2"
}]

java - reflection: How to Override private static abstract inner class method?

I have the following class:
class MyClass{
private static final int VERSION_VALUE = 8;
private static final String VERSION_KEY = "versionName";
public boolean myPublicMethod(String str) {
try {
return myPrivateMethod(str, VERSION_KEY, VERSION_VALUE,
new MyInnerClass() {
#Override
public InputStream loadResource(String name) {
//do something important
}
});
}
catch (Exception e) {
}
return false;
}
private boolean myPrivateMethod(String str, String key, int version,
ResourceLoader resourceLoader) throws Exception
{
//do something
}
private static abstract class MyInnerClass {
public abstract InputStream loadResource(String name);
}
}
I want to write unit test for myPrivateMethod for which I need to pass resourceLoader object and override it's loadResource method.
Here is my test method:
#Test
public void testMyPrivateMethod() throws Exception {
Class<?> cls = Class.forName("my.pack.MyClass$MyInnerClass");
Method method = cls.getDeclaredMethod("loadResource", String.class);
//create inner class instance and override method
Whitebox.invokeMethod(myClassObject, "testValue1", "testValue2", "name1", 10, innerClassObject);
}
Note, that I can't change code.
Well, you could use Javassist...
See this question. I haven't tried this, but you can call this method when you want the override:
public <T extends Object> T getOverride(Class<T> cls, MethodHandler handler) {
ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(cls);
factory.setFilter(
new MethodFilter() {
#Override
public boolean isHandled(Method method) {
return Modifier.isAbstract(method.getModifiers());
}
}
);
return (T) factory.create(new Class<?>[0], new Object[0], handler);
}
Well, the problem i see with your code is that you are calling myPublicMethod and you are giving fourth parameter as new MyInnerClass(). Now in your private method fourth parameter is given as ResourceLoader and from your code i see no relation between MyInnerClass and ResourceLoader. So you can try out following code. It might help.
Despite your warning that you cannot change the code i have changed it because i was trying to run your code.
class MyClass{
private static final int VERSION_VALUE = 8;
private static final String VERSION_KEY = "versionName";
public boolean myPublicMethod(String str) {
try {
return myPrivateMethod(str, VERSION_KEY, VERSION_VALUE,
new MyInnerClass() {
#Override
public InputStream loadResource(String name) {
return null;
//do something important
}
});
}
catch (Exception e) {
}
return false;
}
private boolean myPrivateMethod(String str, String key, int version,
MyInnerClass resourceLoader) throws Exception
{
return false;
//do something
}
private static abstract class MyInnerClass {
public abstract InputStream loadResource(String name);
}
}
Hope it helps.

Jackson and generic type reference

I want to use jackson json library for a generic method as follows:
public MyRequest<T> tester() {
TypeReference<MyWrapper<T>> typeRef = new TypeReference<MyWrapper<T>>();
MyWrapper<T> requestWrapper = (MyWrapper<T>) JsonConverter.fromJson(jsonRequest, typeRef);
return requestWrapper.getRequest();
}
public class MyWrapper<T> {
private MyRequest<T> request;
public MyRequest<T> getRequest() {
return request;
}
public void setRequest(MyRequest<T> request) {
this.request = request;
}
}
public class MyRequest<T> {
private List<T> myobjects;
public void setMyObjects(List<T> ets) {
this.myobjects = ets;
}
#NotNull
#JsonIgnore
public T getMyObject() {
return myobjects.get(0);
}
}
Now the problem is that when I call getMyObject() which is inside the request object Jackson returns the nested custom object as a LinkedHashMap. Is there any way in which I specify that T object needs to be returned? For example: if I sent object of type Customer then Customer should be returned from that List?
This is a well-known problem with Java type erasure: T is just a type variable, and you must indicate actual class, usually as Class argument. Without such information, best that can be done is to use bounds; and plain T is roughly same as 'T extends Object'. And Jackson will then bind JSON Objects as Maps.
In this case, tester method needs to have access to Class, and you can construct
JavaType type = mapper.getTypeFactory().
constructCollectionType(List.class, Foo.class)
and then
List<Foo> list = mapper.readValue(new File("input.json"), type);
'JavaType' works !!
I was trying to unmarshall (deserialize) a List in json String to ArrayList java Objects and was struggling to find a solution since days.
Below is the code that finally gave me solution.
Code:
JsonMarshallerUnmarshaller<T> {
T targetClass;
public ArrayList<T> unmarshal(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper.getDeserializationConfig()
.withAnnotationIntrospector(introspector);
mapper.getSerializationConfig()
.withAnnotationIntrospector(introspector);
JavaType type = mapper.getTypeFactory().
constructCollectionType(
ArrayList.class,
targetclass.getClass());
try {
Class c1 = this.targetclass.getClass();
Class c2 = this.targetclass1.getClass();
ArrayList<T> temp = (ArrayList<T>)
mapper.readValue(jsonString, type);
return temp ;
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null ;
}
}
I modified rushidesai1's answer to include a working example.
JsonMarshaller.java
import java.io.*;
import java.util.*;
public class JsonMarshaller<T> {
private static ClassLoader loader = JsonMarshaller.class.getClassLoader();
public static void main(String[] args) {
try {
JsonMarshallerUnmarshaller<Station> marshaller = new JsonMarshallerUnmarshaller<>(Station.class);
String jsonString = read(loader.getResourceAsStream("data.json"));
List<Station> stations = marshaller.unmarshal(jsonString);
stations.forEach(System.out::println);
System.out.println(marshaller.marshal(stations));
} catch (IOException e) {
e.printStackTrace();
}
}
#SuppressWarnings("resource")
public static String read(InputStream ios) {
return new Scanner(ios).useDelimiter("\\A").next(); // Read the entire file
}
}
Output
Station [id=123, title=my title, name=my name]
Station [id=456, title=my title 2, name=my name 2]
[{"id":123,"title":"my title","name":"my name"},{"id":456,"title":"my title 2","name":"my name 2"}]
JsonMarshallerUnmarshaller.java
import java.io.*;
import java.util.List;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
public class JsonMarshallerUnmarshaller<T> {
private ObjectMapper mapper;
private Class<T> targetClass;
public JsonMarshallerUnmarshaller(Class<T> targetClass) {
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper = new ObjectMapper();
mapper.getDeserializationConfig().with(introspector);
mapper.getSerializationConfig().with(introspector);
this.targetClass = targetClass;
}
public List<T> unmarshal(String jsonString) throws JsonParseException, JsonMappingException, IOException {
return parseList(jsonString, mapper, targetClass);
}
public String marshal(List<T> list) throws JsonProcessingException {
return mapper.writeValueAsString(list);
}
public static <E> List<E> parseList(String str, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(str, listType(mapper, clazz));
}
public static <E> List<E> parseList(InputStream is, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(is, listType(mapper, clazz));
}
public static <E> JavaType listType(ObjectMapper mapper, Class<E> clazz) {
return mapper.getTypeFactory().constructCollectionType(List.class, clazz);
}
}
Station.java
public class Station {
private long id;
private String title;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return String.format("Station [id=%s, title=%s, name=%s]", id, title, name);
}
}
data.json
[{
"id": 123,
"title": "my title",
"name": "my name"
}, {
"id": 456,
"title": "my title 2",
"name": "my name 2"
}]

Configurable Values in Enum

I often use this design in my code to maintain configurable values. Consider this code:
public enum Options {
REGEX_STRING("Some Regex"),
REGEX_PATTERN(Pattern.compile(REGEX_STRING.getString()), false),
THREAD_COUNT(2),
OPTIONS_PATH("options.config", false),
DEBUG(true),
ALWAYS_SAVE_OPTIONS(true),
THREAD_WAIT_MILLIS(1000);
Object value;
boolean saveValue = true;
private Options(Object value) {
this.value = value;
}
private Options(Object value, boolean saveValue) {
this.value = value;
this.saveValue = saveValue;
}
public void setValue(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public String getString() {
return value.toString();
}
public boolean getBoolean() {
Boolean booleanValue = (value instanceof Boolean) ? (Boolean) value : null;
if (value == null) {
try {
booleanValue = Boolean.valueOf(value.toString());
}
catch (Throwable t) {
}
}
// We want a NullPointerException here
return booleanValue.booleanValue();
}
public int getInteger() {
Integer integerValue = (value instanceof Number) ? ((Number) value).intValue() : null;
if (integerValue == null) {
try {
integerValue = Integer.valueOf(value.toString());
}
catch (Throwable t) {
}
}
return integerValue.intValue();
}
public float getFloat() {
Float floatValue = (value instanceof Number) ? ((Number) value).floatValue() : null;
if (floatValue == null) {
try {
floatValue = Float.valueOf(value.toString());
}
catch (Throwable t) {
}
}
return floatValue.floatValue();
}
public static void saveToFile(String path) throws IOException {
FileWriter fw = new FileWriter(path);
Properties properties = new Properties();
for (Options option : Options.values()) {
if (option.saveValue) {
properties.setProperty(option.name(), option.getString());
}
}
if (DEBUG.getBoolean()) {
properties.list(System.out);
}
properties.store(fw, null);
}
public static void loadFromFile(String path) throws IOException {
FileReader fr = new FileReader(path);
Properties properties = new Properties();
properties.load(fr);
if (DEBUG.getBoolean()) {
properties.list(System.out);
}
Object value = null;
for (Options option : Options.values()) {
if (option.saveValue) {
Class<?> clazz = option.value.getClass();
try {
if (String.class.equals(clazz)) {
value = properties.getProperty(option.name());
}
else {
value = clazz.getConstructor(String.class).newInstance(properties.getProperty(option.name()));
}
}
catch (NoSuchMethodException ex) {
Debug.log(ex);
}
catch (InstantiationException ex) {
Debug.log(ex);
}
catch (IllegalAccessException ex) {
Debug.log(ex);
}
catch (IllegalArgumentException ex) {
Debug.log(ex);
}
catch (InvocationTargetException ex) {
Debug.log(ex);
}
if (value != null) {
option.setValue(value);
}
}
}
}
}
This way, I can save and retrieve values from files easily. The problem is that I don't want to repeat this code everywhere. Like as we know, enums can't be extended; so wherever I use this, I have to put all these methods there. I want only to declare the values and that if they should be persisted. No method definitions each time; any ideas?
Using an enum to hold configurable values like this looks like an entirely wrong design. Enums are singletons, so effectively you can only have one configuration active at any given time.
An EnumMap sounds more like what you need. It's external to the enum, so you can instantiate as many configurations as you need.
import java.util.*;
public class EnumMapExample {
static enum Options {
DEBUG, ALWAYS_SAVE, THREAD_COUNT;
}
public static void main(String[] args) {
Map<Options,Object> normalConfig = new EnumMap<Options,Object>(Options.class);
normalConfig.put(Options.DEBUG, false);
normalConfig.put(Options.THREAD_COUNT, 3);
System.out.println(normalConfig);
// prints "{DEBUG=false, THREAD_COUNT=3}"
Map<Options,Object> debugConfig = new EnumMap<Options,Object>(Options.class);
debugConfig.put(Options.DEBUG, true);
debugConfig.put(Options.THREAD_COUNT, 666);
System.out.println(debugConfig);
// prints "{DEBUG=true, THREAD_COUNT=666}"
}
}
API links
java.util.EnumMap
A specialized Map implementation for use with enum type keys. All of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created. Enum maps are represented internally as arrays. This representation is extremely compact and efficient.
i tried doing something similar with enum maps and properties files (please see code below). but my enums were simple and only had one value except for an embedded case. i may have something that is more type safe. i will look around for it.
package p;
import java.util.*;
import java.io.*;
public class GenericAttributes<T extends Enum<T>> {
public GenericAttributes(final Class<T> keyType) {
map = new EnumMap<T, Object>(this.keyType = keyType);
}
public GenericAttributes(final Class<T> keyType, final Properties properties) {
this(keyType);
addStringProperties(properties);
}
public Object get(final T key) {
// what does a null value mean?
// depends on P's semantics
return map.containsKey(key) ? map.get(key) : null;
}
public boolean contains(final T key) {
return map.containsKey(key);
}
public void change(final T key, final Object value) {
remove(key);
put(key, value);
}
public Object put(final T key, final Object value) {
if (map.containsKey(key))
throw new RuntimeException("map already contains: " + key);
else
return map.put(key, value);
}
public Object remove(final T key) {
if (!map.containsKey(key))
throw new RuntimeException("map does not contain: " + key);
return map.remove(key);
}
public String toString() {
return toString(defaultEquals, defaultEndOfLine);
}
// maybe we don;t need this stuff
// we have tests for it though
// it might be useful
public String toString(final String equals, final String endOfLine) {
final StringBuffer sb = new StringBuffer();
for (Map.Entry<T, Object> entry : map.entrySet())
sb.append(entry.getKey()).append(equals).append(entry.getValue()).append(endOfLine);
return sb.toString();
}
public Properties toProperties() {
final Properties p = new Properties();
for (Map.Entry<T, Object> entry : map.entrySet())
p.put(entry.getKey().toString(), entry.getValue().toString());
return p;
}
public void addStringProperties(final Properties properties) {
// keep this for strings, but mostly do work in the enum class
// i.e. static GenericAttributes<PA> fromProperties();
// which would use a fromString()
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
final String key = (String) entry.getKey();
final String value = (String) entry.getValue();
addProperty(key, value);
}
}
public void addProperty(final String key, final Object value) {
try {
final T e = Enum.valueOf(keyType, key);
map.put(e, value);
} catch (IllegalArgumentException e) {
System.err.println(key + " is not an enum from: " + keyType);
}
}
public int size() {
return map.size();
}
public static Properties load(final InputStream inputStream,final Properties defaultProperties) {
final Properties p=defaultProperties!=null?new Properties(defaultProperties):new Properties();
try {
p.load(inputStream);
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static Properties load(final File file,final Properties defaultProperties) {
Properties p=null;
try {
final InputStream is=new FileInputStream(file);
p=load(is,defaultProperties);
is.close();
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static void store(final OutputStream outputStream, final Properties properties) {
try {
properties.store(outputStream, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void store(final File file, final Properties properties) {
try {
final OutputStream os = new FileOutputStream(file);
store(os, properties);
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
final Class<T> keyType;
static final String defaultEquals = "=", defaultEndOfLine = "\n";
private final EnumMap<T, Object> map;
public static void main(String[] args) {
}
}
package p;
import static org.junit.Assert.*;
import org.junit.*;
import java.io.*;
import java.util.*;
enum A1 {
foo,bar,baz;
}
enum A2 {
x,y,z;
}
public class GenericAttributesTestCase {
#Test public void testGenericAttributes() {
new GenericAttributes<A1>(A1.class);
}
#Test public void testGenericAttributesKeyTypeProperties() {
final Properties expected=gA1.toProperties();
final GenericAttributes<A1> gA=new GenericAttributes<A1>(A1.class,expected);
final Properties actual=gA.toProperties();
assertEquals(expected,actual);
}
#Test public void testGet() {
final A1 key=A1.foo;
emptyGA1.put(key,null);
final Object actual=emptyGA1.get(key);
assertEquals(null,actual);
}
#Test public void testGetInteger() {
// attributes.add(key,integer);
// assertEquals(integer,attributes.get("key"));
}
#Test public void testContains() {
for(A1 a:A1.values())
assertFalse(emptyGA1.contains(a));
}
#Test public void testChange() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
final Integer expected=43;
emptyGA1.change(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
#Test public void testAdd() {
final A1 key=A1.foo;
final Integer expected=42;
emptyGA1.put(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
#Test public void testRemove() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
emptyGA1.remove(key);
assertFalse(emptyGA1.contains(key));
}
#Test public void testToString() {
final String actual=gA1.toString();
final String expected="foo=a foo value\nbar=a bar value\n";
assertEquals(expected,actual);
}
#Test public void testToStringEqualsEndOfLine() {
final String equals=",";
final String endOFLine=";";
final String actual=gA1.toString(equals,endOFLine);
final String expected="foo,a foo value;bar,a bar value;";
assertEquals(expected,actual);
}
#Test public void testEmbedded() {
final String equals=",";
final String endOfLine=";";
//System.out.println("toString(\""+equals+"\",\""+endOFLine+"\"):");
final String embedded=gA1.toString(equals,endOfLine);
GenericAttributes<A2> gA2=new GenericAttributes<A2>(A2.class);
gA2.put(A2.x,embedded);
//System.out.println("embedded:\n"+gA2);
// maybe do file={name=a.jpg;dx=1;zoom=.5}??
// no good, key must be used more than once
// so file:a.jpg={} and hack
// maybe file={name=...} will work
// since we have to treat it specially anyway?
// maybe this is better done in ss first
// to see how it grows?
}
#Test public void testFromString() {
// final Attributes a=Attributes.fromString("");
// final String expected="";
// assertEquals(expected,a.toString());
}
#Test public void testToProperties() {
final Properties expected=new Properties();
expected.setProperty("foo","a foo value");
expected.setProperty("bar","a bar value");
final Properties actual=gA1.toProperties();
assertEquals(expected,actual);
}
#Test public void testAddProperties() {
final Properties p=gA1.toProperties();
final GenericAttributes<A1> ga=new GenericAttributes<A1>(A1.class);
ga.addStringProperties(p);
// assertEquals(ga1,ga); // fails since we need to define equals!
// hack, go backwards
final Properties p2=ga.toProperties();
assertEquals(p,p2); // hack until we define equals
}
#Test public void testStore() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final byte[] bytes=baos.toByteArray();
final ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
#Test public void testLoad() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
#Test public void testMain() {
// fail("Not yet implemented");
}
GenericAttributes<A1> gA1=new GenericAttributes<A1>(A1.class);
{
gA1.put(A1.foo,"a foo value");
gA1.put(A1.bar,"a bar value");
}
GenericAttributes<A1> emptyGA1=new GenericAttributes<A1>(A1.class);
}
answering your comment:
seems like i am getting values by using the enum as the key. i am probably confused.
an enum can implement an interface and each set of enums could have an instance of that base class and delegate calls to it (see item 34 of http://java.sun.com/docs/books/effective/toc.html)
i found the other code that went with my generic attributes (please see below), but i can't find any tests for it and am not quite sure what i was doing other than perhaps to add some stronger typing.
my motivation for all of this was to store some attributes for a photo viewer like picasa, i wanted to store a bunch of attributes for a picture in a single line of a property file
package p;
import java.util.*;
public enum GA {
// like properties, seems like this wants to be constructed with a set of default values
i(Integer.class) {
Integer fromString(final String s) {
return new Integer(s);
}
Integer fromNull() {
return zero; // return empty string?
}
},
b(Boolean.class) {
Boolean fromString(final String s) {
return s.startsWith("t")?true:false;
}
Boolean fromNull() {
return false;
}
},
d(Double.class) {
Double fromString(final String s) {
return new Double(s);
}
Double fromNull() {
return new Double(zero);
}
};
GA() {
this(String.class);
}
GA(final Class clazz) {
this.clazz=clazz;
}
abstract Object fromString(String string);
abstract Object fromNull();
static GenericAttributes<GA> fromProperties(final Properties properties) {
final GenericAttributes<GA> pas=new GenericAttributes<GA>(GA.class);
for(Map.Entry<Object,Object> entry:properties.entrySet()) {
final String key=(String)entry.getKey();
final GA pa=valueOf(key);
if(pa!=null) {
final String stringValue=(String)entry.getValue();
Object value=pa.fromString(stringValue);
pas.addProperty(key,value);
} else throw new RuntimeException(key+"is not a member of "+"GA");
}
return pas;
}
// private final Object defaultValue; // lose type?; require cast?
/* private */final Class clazz;
static final Integer zero=new Integer(0);
}
If you are still looking for answers, you could give a try to Properties library which is open-source with MIT license. Using this, you won't have to specify string constants and everything will be determined by an enum defined by you. And, it has some other features too. Highlights of this library are:
All property keys are defined in a single place, i.e. a user defined enum
Property values can contain variables (starting with $ sign, e.g. $PATH) where PATH is a property key in same file
Property value can be obtained as specified data type, so no need to convert string value to required data type
Property value can be obtained as list of specified data types
Property value can be a multi-line text
Can make property keys mandatory or optional
Can specify default value for the property key if value is not available
Is thread safe
You can find sample programs here

Categories

Resources