Xstream : unmarshalling inner classes - java

I would like to marshall and unmarshall objects whose fields are of their class's inner class (synthetic class if I'm not wrong).
class A {
private B field_b=null;
public static class B {
public static B B1 = new B("b1");
public static B B2 = new B("b2");
private final String name;
private B(String name) {
this.name=name;
}
}
public B getBforName(String name) {
if (B1.name.equals(name) return B1;
else if (B2.name.equals(name) return B2;
else return null;
}
And produce and read from an XML:
<A>
<field_B>b1</field_B>
</A>
The writing part is easy.
The reading part is more complicated.
I would like to write a converter:
public class BConverter implements Converter {
public boolean canConvert(Class type) {
return B.class.isAssignableFrom(type) ;
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
// ...
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
A parent_v1 = (A) context.getCurrentObject(); // !!! always empty
A parent_v2 = (A) context.get("current_unmarshalled_A");
return parent_v2.getBforName((String)reader.getValue());
}
}
The context.getCurrentObject() returns null and seems to be obsolete (from forums I read).
The context.get("current_unmarshalled_A")would require that I put in this unMarshallingContext that key and the A object being unmarshalled. I don't find to do that without writing an AConverter. And that is not neat as I would loose the default unmarshalling behaviour for the class A.
Anyone has an idea ?

I'm not sure this is cleanest way to that but it works.
I use a static method in the B class ...
class A {
private B field_b=null;
public static class B {
public static B B1 = new B("b1");
public static B B2 = new B("b2");
public static B getForName(String name) {
if (B1.name.equals(name) return B1;
else if (B2.name.equals(name) return B2;
else return null;
}
private final String name;
private B(String name) {
this.name=name;
}
}
... and reflection in the Converter
public class BConverter implements Converter {
public boolean canConvert(Class type) {
return B.class.isAssignableFrom(type) ;
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
writer.setValue(((B)source).getName())
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
try {
Method method = context.getRequiredType().getMethod("getForName", String.class);
final String v = reader.getValue();
Object b= method.invoke(null, v);
if (b== null)
throw new ConversionException("Could not retrieve a B object for \"" + v + "\"");
return b;
} catch (Exception ex) {
throw new ConversionException("Error while retrieving a B object", ex);
}
}
}

Related

Java Unchecked call to raw type

I'm trying to create a Handler interface, which is able to handle different types of events based on their types. I'm having trouble with the following warning:
Unchecked call to 'handle(T)' as a member of raw type 'Handler'
Here are my classes.
public interface Handler<T> {
void handle(T event); }
public class IntegerHandler implements Handler<Integer> {
#Override
public void handle(Integer event) {
System.out.println("Integer: " + event);
}
}
public class ObjectHandler implements Handler<Object> {
#Override
public void handle(Object event) {
System.out.println("Object: " + event);
}
}
public class StringHandler implements Handler<String> {
#Override
public void handle(String event) {
System.out.println("String: " + event);
}
}
public class TestHandlers {
public static void main(String[] args) {
String a = "hello";
Integer b = 12;
Long c = 23L;
dispatch(a).handle(a);
dispatch(b).handle(b);
dispatch(c).handle(c);
}
private static Handler dispatch(Object o) {
if (o instanceof String) {
return new StringHandler();
} else if (o instanceof Integer) {
return new IntegerHandler();
} else {
return new ObjectHandler();
}
}
}
The output looks correct:
String: hello
Integer: 12
Object: 23
I guess the problem is that my dispatch method is returning a unchecked version of Handler.
Not sure what the proper way is to do this right.
Create a factory (credit to Generic Factory With Unknown Implementation Classes)
public class HandlerFactory<H extends Handler> {
final Class<H> handlerClass;
protected HandlerFactory(final Class<H> clazz) {
handlerClass = clazz;
}
protected H create() throws InstantiationException, IllegalAccessException {
H handler = handlerClass.newInstance();
return handler;
}
public static <H extends Handler> HandlerFactory<H> createFactory(final Class<H> clazz) throws InstantiationException, IllegalAccessException {
return new HandlerFactory(clazz);
}
public static Handler dispatch(Object obj) throws InstantiationException, IllegalAccessException {
Class c;
if (obj instanceof String) {
c = StringHandler.class;
} else if (obj instanceof Integer) {
c = IntegerHandler.class;
} else {
c = ObjectHandler.class;
}
HandlerFactory factory = HandlerFactory.createFactory(c);
return factory.create();
}
}
Exception handlers not shown:
...
String a = "hello";
Integer b = 12;
Long c = 23L;
HandlerFactory.dispatch(a).handle(a);
HandlerFactory.dispatch(b).handle(b);
HandlerFactory.dispatch(c).handle(c);

Select method based on field in class

So I have a class that contains a String-field:
public class A {
private String type = ...
public String getType(){
return this.type;
}
public void setType(String type){
this.type = type;
}
}
I also have a list of all possible types, there are twelve and possibly more in the future.
Now I want to write a method that gets an object of class A and calls a specific method depending on which "type" is in the class.
Is there a smarter solution than writing 12 (or more) if-statements?
Normally I would use the Visitor-pattern but I don't want to create twelve new classes.
edit:
I ended up creating a
Map<String,Function<A,String>> map = new HashMap<String,Function<A,String>>
and then call
A a;
...
map.get(a.getType).apply(a);
Instead of storing type as a "free-form" text value, you should be using an enum, since you have a well-defined list of values.
You can even have the different enums implement the same method differently, by using an abstract method. This will allow you to totally eliminate the error-prone switch statements.
Below is an example showing both instance values and abstract methods. The pattern shown will keep the implementation out of the enum, while having the compiler catch all uses when a new enum is added.
public enum Type {
INTEGER("Integer") {
#Override
public void apply(Action action, A a) {
action.applyInteger(a);
}
},
STRING ("Text") {
#Override
public void apply(Action action, A a) {
action.applyString(a);
}
};
private String displayName;
private Type(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return this.displayName;
}
public abstract void apply(Action action, A a);
}
public interface Action {
public void applyInteger(A a);
public void applyString(A a);
}
public class A {
private Type type;
public Type getType(){
return this.type;
}
public void setType(Type type){
this.type = type;
}
public void apply(Action action) {
this.type.apply(action, this);
}
}
When you add a new type to the TYPE enum, you also add a new method to the Action interface, which will force you to implement that method in all implementations of the interface. With switch statements, you'd get no such safety.
If you are using JDK 7 or greater go for a switch which accepts String as a parameter and write cases for each.
switch (type) {
case "SomeX":
yourInstance.invokeMethod();
break;
case "SomeY":
...
I guess the other answers are correct but, by reading the question I think the more direct answer will be using introspection and convention:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static class A {
private String type;
public String getType(){
return this.type;
}
public void setType(String type){
this.type = type;
}
}
public static class Actions {
public void runForType1(A a) {
System.out.println("It's type 1");
}
public void runForType2(A a) {
System.out.println("It's type 2");
}
public void runForType3(A a) {
System.out.println("It's type 3");
}
}
public static class Runner {
Actions actions;
public Runner(Actions a) {
this.actions = a;
}
public void run(A a) {
try {
Method m = actions.getClass().getMethod("runFor" + a.getType(), A.class);
m.invoke(actions, a);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Runner r = new Runner(new Actions());
A type1 = new A();
type1.setType("Type1");
A type2 = new A();
type2.setType("Type2");
A type3 = new A();
type3.setType("Type3");
r.run(type1);
r.run(type2);
r.run(type3);
}
}
expected output for the example will be:
It's type 1
It's type 2
It's type 3
If convention is not possible you can always create a HashMap with a type to method name mapping.

Xstream field with converter is not an attribute even with #XStreamAsAttribute

I try to marshal an object and I want all the fields to be attributes. The normal fields are OK with the #XStreamAsAttribute annotation but I have two of them with a converter. For them when I marshal they are converted as field...
#XStreamAlias(value="sinistre")
public class ObjetMetierSinistreDto {
#XStreamAlias(value="S_sinistreEtat")
#XStreamAsAttribute
private String etat;
#XStreamAsAttribute
#XStreamAlias(value="S_sinistreDateSurv")
#XStreamConverter(value=JodaDateConverter.class)
private LocalDate dateSurvenanceDossier;
...
The converter:
public class JodaDateConverter implements Converter {
#Override
#SuppressWarnings("unchecked")
public boolean canConvert(final Class type) {
return (type != null) && LocalDate.class.getPackage().equals(type.getPackage());
}
#Override
public void marshal(final Object source, final HierarchicalStreamWriter writer,
final MarshallingContext context) {
writer.setValue(source.toString().replace("-", "/"));
}
#Override
#SuppressWarnings("unchecked")
public Object unmarshal(final HierarchicalStreamReader reader,
final UnmarshallingContext context) {
try {
final Class requiredType = context.getRequiredType();
final Constructor constructor = requiredType.getConstructor(Object.class);
return constructor.newInstance(reader.getValue());
} catch (final Exception e) {
throw new RuntimeException(String.format(
"Exception while deserializing a Joda Time object: %s", context.getRequiredType().getSimpleName()), e);
}
}
}
and the result:
<sinistre S_sinistreEtat="S">
<S_sinistreDateSurv>2015/02/01</S_sinistreDateSurv>
</sinistre>
and what I like:
<sinistre S_sinistreEtat="S"
S_sinistreDateSurv="2015/02/01"/>
I finally found how to solve this problem!
The JodaDateConverter should not implements Converter but extends AbstractSingleValueConverter (as the DateConverter from XStream)
Then you just need to override canConvert() and fromString() and you are good to go!
Exemple:
public class JodaDateConverter extends AbstractSingleValueConverter {
#Override
#SuppressWarnings("unchecked")
public boolean canConvert(final Class type) {
return (type != null) && LocalDate.class.getPackage().equals(type.getPackage());
}
#Override
public Object fromString(String str) {
String separator;
if(str.contains(":")){
separator = ":";
} else if(str.contains("/")){
separator = "/";
} else if(str.contains("-")){
separator = "-";
} else {
throw new RuntimeException("The date must contains ':' or '/' or '-'");
}
String[] date = str.split(separator);
if(date.length < 3){
throw new RuntimeException("The date must contains hour, minute and second");
}
return new LocalDate(Integer.valueOf(date[0]),Integer.valueOf(date[1]),Integer.valueOf(date[2]));
}
}

GSON won't properly serialise a class that extends HashMap

I have the following code:
public static class A
{
public A() {}
private List<B> bs = new ArrayList<B>();
public List<B> getBs() {
return bs;
}
public void setBs(List<B> bs) {
this.bs = bs;
}
}
public static class B
{
B(String foo){this.foo=foo;}
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
public static void main(String[] args) throws Exception {
Gson gson = new Gson();
A a = new A();
a.getBs().add(new B("bar"));
System.out.println(gson.toJson(a));
}
and as expected the output is:
{"bs":[{"foo":"bar"}]}
However, if I make A a subclass of HashMap:
public static class A extends HashMap
I get an empty set returned: {}
I have even tried:
System.out.println(gson.toJson(a, new TypeToken<A>(){}.getType()));
and:
System.out.println(gson.toJson(a, new TypeToken<HashMap>(){}.getType()));
Can someone tell me whether/how I can serialise this HashMap subclass using GSON?
Gson works with (default and custom) TypeAdapterFactory instances and the TypeAdapter objects they create to serialize/deserialize your objects.
It goes through the list of registered TypeAdapterFactory objects and picks the first one that can create an appropriate TypeAdapter for the type of the object your are providing. One of these TypeAdapterFactory objects, is one of type MapTypeAdapterFactory which creates a TypeAdapter (of type MapTypeAdapterFactory$Adapter) that serializes/deserializes based on the java.util.Map interface (keys/values). It does nothing about your custom sub type's fields.
If you want Gson to serialize your type as both a Map and a custom type, you will need to register either a custom TypeAdapter directly or a custom TypeAdapterFactory that creates TypeAdapter objects.
Here is the custom TypeAdapterFactory.
Test:
public static void main(String[] args) throws Exception{
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new RetainFieldMapFactory())
.create();
Foo f = gson.fromJson("{'key1':'value1','key2':'value2'}", Foo.class);
System.out.println("in map:\t" + f.toString());
System.out.println("f.key1:\t"+f.key1);
System.out.println("toJson:\t"+gson.toJson(f));
}
public static class Foo extends HashMap<String, String> {
private String key1;
}
Output:
in map: {key2=value2}
f.key1: value1
toJson: {"key2":"value2","key1":"value1"}
RetainFieldMapFactory.java:
/**
* Created by linfaxin on 2015/4/9 009.
* Email: linlinfaxin#163.com
*/
public class RetainFieldMapFactory implements TypeAdapterFactory {
FieldNamingPolicy fieldNamingPolicy = FieldNamingPolicy.IDENTITY;
ConstructorConstructor constructorConstructor = new ConstructorConstructor(Collections.<Type, InstanceCreator<?>>emptyMap());
MapTypeAdapterFactory defaultMapFactory = new MapTypeAdapterFactory(constructorConstructor, false);
ReflectiveFilterMapFieldFactory defaultObjectFactory = new ReflectiveFilterMapFieldFactory(constructorConstructor,
fieldNamingPolicy, Excluder.DEFAULT);
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> mapAdapter = defaultMapFactory.create(gson, type);
if(mapAdapter!=null){
return (TypeAdapter<T>) new RetainFieldMapAdapter(mapAdapter, defaultObjectFactory.create(gson, type));
}
return mapAdapter;
}
class RetainFieldMapAdapter extends TypeAdapter<Map<String, Object>>{
TypeAdapter<Map<String, Object>> mapAdapter;
ReflectiveTypeAdapterFactory.Adapter<Map<String, Object>> objectAdapter;
RetainFieldMapAdapter(TypeAdapter mapAdapter, ReflectiveTypeAdapterFactory.Adapter objectAdapter) {
this.mapAdapter = mapAdapter;
this.objectAdapter = objectAdapter;
}
#Override
public void write(final JsonWriter out, Map<String, Object> value) throws IOException {
//1.write object
StringWriter sw = new StringWriter();
objectAdapter.write(new JsonWriter(sw), value);
//2.convert object to a map
Map<String, Object> objectMap = mapAdapter.fromJson(sw.toString());
//3.overwrite fields in object to a copy map
value = new LinkedHashMap<String, Object>(value);
value.putAll(objectMap);
//4.write the copy map
mapAdapter.write(out, value);
}
#Override
public Map<String, Object> read(JsonReader in) throws IOException {
//1.create map, all key-value retain in map
Map<String, Object> map = mapAdapter.read(in);
//2.create object from created map
Map<String, Object> object = objectAdapter.fromJsonTree(mapAdapter.toJsonTree(map));
//3.remove fields in object from map
for(String field : objectAdapter.boundFields.keySet()){
map.remove(field);
}
//4.put map to object
object.putAll(map);
return object;
}
}
/**
* If class is extends from some custom map,
* class should implement this to avoid serialize custom map's fields
*/
public interface RetainFieldFlag {}
static class ReflectiveFilterMapFieldFactory extends ReflectiveTypeAdapterFactory{
public ReflectiveFilterMapFieldFactory(ConstructorConstructor constructorConstructor, FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
super(constructorConstructor, fieldNamingPolicy, excluder);
}
#Override
protected boolean shouldFindFieldInClass(Class willFindClass, Class<?> originalRaw) {
if(RetainFieldFlag.class.isAssignableFrom(originalRaw)){
return RetainFieldFlag.class.isAssignableFrom(willFindClass);
}else{
Class[] endClasses = new Class[]{Object.class, HashMap.class, LinkedHashMap.class,
LinkedTreeMap.class, Hashtable.class, TreeMap.class, ConcurrentHashMap.class,
IdentityHashMap.class, WeakHashMap.class, EnumMap.class};
for(Class c : endClasses){
if(willFindClass == c) return false;
}
}
return super.shouldFindFieldInClass(willFindClass, originalRaw);
}
}
/**
* below code copy from {#link com.google.gson.internal.bind.ReflectiveTypeAdapterFactory}
* (little modify, in source this class is final)
* Type adapter that reflects over the fields and methods of a class.
*/
static class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
private final FieldNamingStrategy fieldNamingPolicy;
private final Excluder excluder;
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
this.constructorConstructor = constructorConstructor;
this.fieldNamingPolicy = fieldNamingPolicy;
this.excluder = excluder;
}
public boolean excludeField(Field f, boolean serialize) {
return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
}
private String getFieldName(Field f) {
SerializedName serializedName = f.getAnnotation(SerializedName.class);
return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
}
public <T> Adapter<T> create(Gson gson, final TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
if (!Object.class.isAssignableFrom(raw)) {
return null; // it's a primitive!
}
ObjectConstructor<T> constructor = constructorConstructor.get(type);
return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
}
private ReflectiveTypeAdapterFactory.BoundField createBoundField(
final Gson context, final Field field, final String name,
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
// special casing primitives here saves ~5% on Android...
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
#SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
#Override void write(JsonWriter writer, Object value)
throws IOException, IllegalAccessException {
Object fieldValue = field.get(value);
TypeAdapter t = new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
t.write(writer, fieldValue);
}
#Override void read(JsonReader reader, Object value)
throws IOException, IllegalAccessException {
Object fieldValue = typeAdapter.read(reader);
if (fieldValue != null || !isPrimitive) {
field.set(value, fieldValue);
}
}
};
}
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) {
return result;
}
Type declaredType = type.getType();
Class<?> originalRaw = type.getRawType();
while (shouldFindFieldInClass(raw, originalRaw)) {
Field[] fields = raw.getDeclaredFields();
for (Field field : fields) {
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
if (!serialize && !deserialize) {
continue;
}
field.setAccessible(true);
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
BoundField boundField = createBoundField(context, field, getFieldName(field),
TypeToken.get(fieldType), serialize, deserialize);
BoundField previous = result.put(boundField.name, boundField);
if (previous != null) {
throw new IllegalArgumentException(declaredType
+ " declares multiple JSON fields named " + previous.name);
}
}
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
}
return result;
}
protected boolean shouldFindFieldInClass(Class willFindClass, Class<?> originalRaw){
return willFindClass != Object.class;
}
static abstract class BoundField {
final String name;
final boolean serialized;
final boolean deserialized;
protected BoundField(String name, boolean serialized, boolean deserialized) {
this.name = name;
this.serialized = serialized;
this.deserialized = deserialized;
}
abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
}
public static final class Adapter<T> extends TypeAdapter<T> {
private final ObjectConstructor<T> constructor;
private final Map<String, BoundField> boundFields;
private Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
this.constructor = constructor;
this.boundFields = boundFields;
}
#Override public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
T instance = constructor.construct();
try {
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
in.skipValue();
} else {
field.read(in, instance);
}
}
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
in.endObject();
return instance;
}
#Override public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
try {
for (BoundField boundField : boundFields.values()) {
if (boundField.serialized) {
out.name(boundField.name);
boundField.write(out, value);
}
}
} catch (IllegalAccessException e) {
throw new AssertionError();
}
out.endObject();
}
}
}
static class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
private final Gson context;
private final TypeAdapter<T> delegate;
private final Type type;
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
this.context = context;
this.delegate = delegate;
this.type = type;
}
#Override
public T read(JsonReader in) throws IOException {
return delegate.read(in);
}
#SuppressWarnings({"rawtypes", "unchecked"})
#Override
public void write(JsonWriter out, T value) throws IOException {
// Order of preference for choosing type adapters
// First preference: a type adapter registered for the runtime type
// Second preference: a type adapter registered for the declared type
// Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
// Fourth preference: reflective type adapter for the declared type
TypeAdapter chosen = delegate;
Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
if (runtimeType != type) {
TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for the runtime type, so we will use that
chosen = runtimeTypeAdapter;
} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
// The user registered a type adapter for Base class, so we prefer it over the
// reflective type adapter for the runtime type
chosen = delegate;
} else {
// Use the type adapter for runtime type
chosen = runtimeTypeAdapter;
}
}
chosen.write(out, value);
}
/**
* Finds a compatible runtime type if it is more specific
*/
private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
if (value != null
&& (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
type = value.getClass();
}
return type;
}
}
}

Java factory pattern - load classes dynamically

I have a lot of classes UNO,HAV,MAS,KOS
I want to create a factory pattern.
validator.load("UNO").validate();
I need dynamically load classes into validator class and return an instance.
(dynamically set name of the class and return an instance)
My problem is: how can I return the instance of a class, if I have incompatible types?
I don't know what to write in return type of method.
The main problem in the Validator CLASS.
public SegmentAbstract load(String str) {
AND
return SegmentAbsClass.forName(identify);
Main class
try{
validator.load("UNO").validate();
}catch(Exception e){
System.out.print("No class ");
}
Abstract Class (SegmentAbstract)
public abstract class SegmentAbstract {
public abstract Boolean validate();
}
Class UNO
public class UNA extends SegmentAbstract{
public Boolean validate() {
System.out.print("UNO!!");
return true;
}
}
Class Validator
public class Validator {
public SegmentAbstract load(String str) {
String identify = str.substring(0, 3);
try {
return SegmentAbsClass.forName(identify);
}
catch(Exception e) {
return this;
}
}
}
Try this :
public interface Validator {
boolean validate(Object obj);
}
public final class ValidatorFactory {
private ValidatorFactory(){}
public static Validator load(String type){
try {
Class<?> clazz = Class.forName(type);
if (Arrays.asList(clazz.getInterfaces()).contains(Validator.class)){
return (Validator) clazz.newInstance();
}
throw new IllegalArgumentException("Provided class doesn't implement Validator interface");
} catch (Exception e) {
throw new IllegalArgumentException("Wrong class provided", e);
}
}
}
Maybe this will help???
I will do something like that:
// ISegment.java
public interface ISegment {
Boolean validate();
}
// Uno.java
public class Uno implements ISegment {
public Boolean validate() {
System.out.print("UNO!!");
return true;
}
}
// SegmentFactory.java
public final class SegmentFactory {
public static enum Supported {
UNO("uno", Uno.class), /* ... */, HAV("hav", Hav.class);
private final Class<?> clazz;
private final String name;
private Supported(final String name, final Class<?> clazz) {
this.name = name;
this.clazz = clazz;
}
public Class<?> getClazz() {
return clazz;
}
public static Supported for(final String name) {
for (final Supported s : values()) {
if (s.name.equals(name) {
return s;
}
}
return null; // a default one
}
}
public static ISegment create(final Supported supp) {
if (supp == null) {
return null;
}
return supp.getClazz.newInstance();
}
private SegmentFactory() {
// avoid instantiation
}
}
usage:
final ISegment sa = SegmentFactory.create(SegmentFactory.Supported.for("uno"));
sa.validate();
Not tested!!
Take a look here. Briefly, the idea is to create a map in your factory class (Map<String,String>, key is identifier, value is fully qualified class name), and add supported classes during initialization. Then you use reflection to instantiate an object in your factory method. Also, you can avoid reflection by using Map<String, SegmentAbstract> instead of Map<String,String> and adding public abstract getNewSegment() to your SegmentAbstract class.

Categories