Browse files

- Basic object factory

  • Loading branch information...
1 parent 2619428 commit b166beaff82cef21bde9f05c0d479dbb48a1f370 @sushantk committed Sep 28, 2012
Showing with 977 additions and 9 deletions.
  1. +5 −0 ants/.classpath
  2. BIN ants/external/jackson-all-1.9.9.jar
  3. BIN ants/external/slf4j-api-1.7.1.jar
  4. BIN ants/external/slf4j-simple-1.7.1.jar
  5. +20 −0 ants/src/ants/Const.java
  6. +323 −0 ants/src/ants/ObjectFactory.java
  7. +23 −0 ants/src/ants/ObjectTree.java
  8. +6 −0 ants/src/ants/Result.java
  9. +36 −0 ants/src/ants/StringDefault.java
  10. +26 −0 ants/src/ants/StringLiteral.java
  11. +8 −1 ants/src/ants/TaskManager.java
  12. +4 −4 ants/src/ants/Util.java
  13. +9 −0 ants/src/ants/annotation/ConfigurableClass.java
  14. +14 −0 ants/src/ants/annotation/ConfigurableMethod.java
  15. +21 −0 ants/src/ants/api/Configurable.java
  16. +40 −0 ants/src/ants/api/Context.java
  17. +1 −1 ants/src/ants/{ → api}/Data.java
  18. +8 −0 ants/src/ants/api/IConfigurable.java
  19. +8 −0 ants/src/ants/api/IList.java
  20. +9 −0 ants/src/ants/api/IParams.java
  21. +7 −0 ants/src/ants/api/IString.java
  22. +69 −0 ants/src/ants/api/ParamsDefault.java
  23. +7 −0 ants/src/ants/api/RequestContext.java
  24. +7 −3 ants/src/ants/{ → api}/Task.java
  25. +12 −0 ants/src/ants/exception/ObjectConfigureException.java
  26. +8 −0 ants/src/ants/exception/ObjectCreateException.java
  27. +8 −0 ants/src/ants/exception/ObjectIncompleteException.java
  28. +12 −0 ants/src/ants/exception/ObjectParseException.java
  29. +251 −0 ants/test/ants/test/ObjectFactoryTest.java
  30. +17 −0 ants/test/ants/test/data/ObjectFactory/childObjectTree.json
  31. +8 −0 ants/test/ants/test/data/ObjectFactory/paramsArrayTree.json
  32. +10 −0 ants/test/ants/test/data/ObjectFactory/paramsMapTree.json
View
5 ants/.classpath
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="external/slf4j-api-1.7.1.jar"/>
+ <classpathentry kind="lib" path="external/slf4j-simple-1.7.1.jar"/>
+ <classpathentry kind="lib" path="external/jackson-all-1.9.9.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="bin"/>
</classpath>
View
BIN ants/external/jackson-all-1.9.9.jar
Binary file not shown.
View
BIN ants/external/slf4j-api-1.7.1.jar
Binary file not shown.
View
BIN ants/external/slf4j-simple-1.7.1.jar
Binary file not shown.
View
20 ants/src/ants/Const.java
@@ -0,0 +1,20 @@
+package ants;
+
+public final class Const {
+
+ public static class thefault {
+ public static final String StringClass = "ants.StringDefault";
+ public static final String ParamsClass = "ants.ParamsDefault";
+ }
+
+ public static class attribute {
+ public static final String id = "@id";
+ public static final String klass = "@class";
+ }
+
+ public static final String comma = ",";
+
+ public static final String set = "set";
+ public static final String setValue = "setValue";
+ public static final String setList = "setList";
+}
View
323 ants/src/ants/ObjectFactory.java
@@ -0,0 +1,323 @@
+package ants;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.map.MappingJsonFactory;
+import org.codehaus.jackson.node.JsonNodeFactory;
+import org.codehaus.jackson.node.ObjectNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ants.annotation.ConfigurableClass;
+import ants.annotation.ConfigurableMethod;
+import ants.api.IConfigurable;
+import ants.exception.ObjectConfigureException;
+import ants.exception.ObjectCreateException;
+import ants.exception.ObjectIncompleteException;
+import ants.exception.ObjectParseException;
+
+public class ObjectFactory {
+
+ static final Logger logger = LoggerFactory.getLogger(ObjectFactory.class);
+ static JsonFactory jsonMapper = new MappingJsonFactory();
+ static JsonNodeFactory jsonFactory = JsonNodeFactory.instance;
+
+ static int autoId = 0;
+
+ /**
+ * parse a json string into an object tree.
+ *
+ * @throws ObjectParseException
+ */
+ public ObjectTree parse(String json) throws ObjectParseException {
+ try {
+ return this.parse(json,
+ new ByteArrayInputStream(json.getBytes("utf-8")));
+ } catch (UnsupportedEncodingException ex) {
+ // ignored, we dont expect this with utf-8
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * parse a json stream into an object tree.
+ *
+ * @throws ObjectParseException
+ */
+ public ObjectTree parse(String identifier, InputStream json)
+ throws ObjectParseException {
+ JsonNode node = null;
+ try {
+ JsonParser parser = jsonMapper.createJsonParser(json);
+ node = parser.readValueAsTree();
+ } catch (IOException ex) {
+ throw new ObjectParseException("Failed to parse JSON: "
+ + identifier, ex);
+ }
+
+ return new ObjectTree(node);
+ }
+
+ /**
+ * create and configure the object with the given object tree
+ *
+ * @throws ObjectConfigureException
+ */
+ public IConfigurable configure(ObjectTree tree, String defaultClass,
+ String tagName, String id, String defaultListItemClass,
+ String listItemTag) throws ObjectConfigureException {
+ Stack<String> tagStack = new Stack<String>();
+
+ Exception ex = null;
+ try {
+ return this.configure(tree.getNode(), defaultClass, tagName,
+ id, defaultListItemClass, listItemTag, tagStack);
+ } catch (SecurityException e) {
+ ex = e;
+ } catch (IllegalArgumentException e) {
+ ex = e;
+ } catch (NoSuchMethodException e) {
+ ex = e;
+ } catch (IllegalAccessException e) {
+ ex = e;
+ } catch (InvocationTargetException e) {
+ ex = e;
+ } catch (ObjectCreateException e) {
+ ex = e;
+ } catch (ObjectIncompleteException e) {
+ ex = e;
+ } catch (ClassNotFoundException e) {
+ ex = e;
+ } catch (InstantiationException e) {
+ ex = e;
+ }
+
+ String tagStackString = Util.implode(tagStack, "=>");
+ throw new ObjectConfigureException("Failed to configure: "
+ + tagStackString, ex);
+ }
+
+ /**
+ * Internal method to create and configure the object recursively with the
+ * given node
+ */
+ private IConfigurable configure(JsonNode node,
+ String defaultClass, String tagName, String id,
+ String defaultListItemClass, String listItemTag,
+ Stack<String> tagStack)
+ throws ObjectCreateException, ObjectIncompleteException,
+ SecurityException, IllegalArgumentException, NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException,
+ ClassNotFoundException, InstantiationException {
+
+ IConfigurable object = ObjectFactory.create(node, defaultClass,
+ tagName, id);
+ tagStack.push(ObjectFactory.toString(object));
+
+ logger.debug("Configuring node: {}", tagStack.peek());
+ Class<?> klass = object.getClass();
+ ConfigurableClass kannotation = klass.getAnnotation(ConfigurableClass.class);
+ if (node.isValueNode()) {
+ Method method = klass.getMethod(Const.setValue, String.class);
+ method.invoke(object, node.asText());
+ } else if (node.isArray() || ((null != kannotation) && kannotation.expectsList())) {
+ this.configureWithList(object, node, defaultListItemClass, listItemTag, tagStack);
+ } else if (node.isObject()) {
+ this.configureWithObjects(object, (ObjectNode) node, tagStack);
+ }
+
+ tagStack.pop();
+ return object;
+ }
+
+ /**
+ * Internal method to configure the object recursively with the given node's
+ * children, treating them as child objects
+ */
+ private void configureWithObjects(IConfigurable object, ObjectNode node,
+ Stack<String> tagStack) throws ObjectCreateException,
+ ObjectIncompleteException, SecurityException,
+ IllegalArgumentException, NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException,
+ ClassNotFoundException, InstantiationException {
+ HashSet<String> setMethods = new HashSet<String>();
+
+ Class<?> klass = object.getClass();
+ Iterator<Entry<String, JsonNode>> it = node.getFields();
+ while (it.hasNext()) {
+ Entry<String, JsonNode> entry = it.next();
+ String childTagName = entry.getKey();
+ if (childTagName.startsWith("@")) {
+ continue; // attributes
+ // TODO: set attribute on parent
+ }
+
+ String setMethodName = Const.set
+ + childTagName.substring(0, 1).toUpperCase()
+ + childTagName.substring(1);
+ Method method = klass.getMethod(setMethodName, IConfigurable.class);
+ ConfigurableMethod annotation = method
+ .getAnnotation(ConfigurableMethod.class);
+
+ String defaultClass = "";
+ String defaultListItemClass = "";
+ String listItemTag = "";
+ if(null != annotation) {
+ defaultClass = annotation.defaultClass();
+ defaultListItemClass = annotation.defaultListItemClass();
+ listItemTag = annotation.listItemTag();
+ }
+
+ IConfigurable childObject = this.configure(entry.getValue(),
+ defaultClass, childTagName, "",
+ defaultListItemClass, listItemTag, tagStack);
+ method.invoke(object, childObject);
+
+ setMethods.add(setMethodName);
+ }
+
+ ObjectFactory.verify(object, setMethods);
+ }
+
+ /**
+ * Internal method to configure the object recursively with the given node's
+ * children treating them as a list of child objects
+ */
+ private void configureWithList(IConfigurable object, JsonNode node,
+ String defaultListItemClass, String listItemTag,
+ Stack<String> tagStack)
+ throws ObjectCreateException, ObjectIncompleteException,
+ SecurityException, IllegalArgumentException, NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException,
+ ClassNotFoundException, InstantiationException {
+ Class<?> klass = object.getClass();
+ Method method = klass.getMethod(Const.setList, LinkedHashMap.class);
+ ConfigurableMethod annotation = method
+ .getAnnotation(ConfigurableMethod.class);
+
+ if(listItemTag.isEmpty()) {
+ listItemTag = annotation.listItemTag();
+ }
+
+ LinkedHashMap<String, IConfigurable> childList = new LinkedHashMap<String, IConfigurable>();
+ if (node.isArray()) {
+ Iterator<JsonNode> it = node.getElements();
+ while (it.hasNext()) {
+ IConfigurable childObject = this.configure(it.next(),
+ defaultListItemClass, listItemTag, "", "", "", tagStack);
+ childList.put(childObject.getId(), childObject);
+ }
+ } else if (node.isObject()) {
+ Iterator<Entry<String, JsonNode>> it = node.getFields();
+ while (it.hasNext()) {
+ Entry<String, JsonNode> entry = it.next();
+ String childId = entry.getKey();
+ if (childId.startsWith("@")) {
+ continue; // attribute of the parent object
+ // TODO: set it on parent
+ }
+
+ IConfigurable childObject = this.configure(entry.getValue(),
+ defaultListItemClass, listItemTag, childId, "", "", tagStack);
+ childList.put(childObject.getId(), childObject);
+ }
+ }
+
+ method.invoke(object, childList);
+ }
+
+ /**
+ * Verifies if all required parameters are set on an object after it is
+ * configured
+ */
+ private static void verify(IConfigurable object, Set<String> setMethods)
+ throws ObjectIncompleteException {
+ ArrayList<String> missingParams = new ArrayList<String>();
+
+ Class<?> klass = object.getClass();
+ Method[] methods = klass.getMethods();
+ for (Method method : methods) {
+ ConfigurableMethod annotation = method
+ .getAnnotation(ConfigurableMethod.class);
+ if (null != annotation) {
+ String methodName = method.getName();
+ boolean required = annotation.required();
+ if (required && !setMethods.contains(methodName)) {
+ missingParams.add(methodName.substring(3).toLowerCase());
+ }
+ }
+ }
+
+ if (missingParams.size() > 0) {
+ String error = "Missing required parameters: "
+ + Util.implode(missingParams, Const.comma);
+ throw new ObjectIncompleteException(error);
+ }
+ }
+
+ /**
+ * instantiate an IConfigurable object using the class name specified in the
+ * JsonNode or the provided default class name.
+ */
+ private static IConfigurable create(JsonNode node, String defaultClass,
+ String tagName, String id) throws ObjectCreateException,
+ ClassNotFoundException, SecurityException, NoSuchMethodException,
+ IllegalArgumentException, InstantiationException,
+ IllegalAccessException, InvocationTargetException {
+ String className = defaultClass;
+ if (node.isObject()) {
+ JsonNode classNode = node.get(Const.attribute.klass);
+ if (null != classNode) {
+ className = classNode.asText();
+ }
+ }
+ if (className.isEmpty()) {
+ if (node.isValueNode()) {
+ className = Const.thefault.StringClass;
+ } else {
+ className = Const.thefault.ParamsClass;
+ }
+ }
+
+ if (id.isEmpty()) {
+ JsonNode idNode = node.get(Const.attribute.id);
+ if (null != idNode) {
+ id = idNode.asText();
+ }
+ if(id.isEmpty()) {
+ id = String.format("_id_%d", ObjectFactory.autoId++);
+ }
+ }
+
+ Class<?> klass = Class.forName(className);
+ Constructor<?> tc = klass.getConstructor(String.class, String.class);
+ Object t = tc.newInstance(tagName, id);
+ return (IConfigurable) t;
+ }
+
+ /**
+ * return a string representation of an IConfigurable object for debugging
+ */
+ private static String toString(IConfigurable object) {
+ return object.getTag() + "<" + object.getClass().getName() + ", "
+ + object.getId() + ">";
+ }
+
+}
View
23 ants/src/ants/ObjectTree.java
@@ -0,0 +1,23 @@
+package ants;
+
+import org.codehaus.jackson.JsonNode;
+
+/**
+ * Wrapper over jackson node class
+ */
+public final class ObjectTree {
+
+ JsonNode node;
+
+ public ObjectTree(JsonNode node) {
+ this.node = node;
+ }
+
+ public JsonNode getNode() {
+ return this.node;
+ }
+
+ public String toString() {
+ return this.node.toString();
+ }
+}
View
6 ants/src/ants/Result.java
@@ -0,0 +1,6 @@
+package ants;
+
+public enum Result {
+
+ SUCCESS, FAILED
+}
View
36 ants/src/ants/StringDefault.java
@@ -0,0 +1,36 @@
+package ants;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ants.annotation.ConfigurableMethod;
+import ants.api.Configurable;
+import ants.api.Context;
+import ants.api.IString;
+
+public class StringDefault extends Configurable
+ implements IString {
+ static final Logger logger = LoggerFactory.getLogger(StringDefault.class);
+
+ public StringDefault(String tagName, String id) {
+ super(tagName, id);
+ }
+
+ private String value;
+
+ @ConfigurableMethod(required=true)
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue(Context context) {
+ return StringDefault.evaluate(context, this.value);
+ }
+
+ public static String evaluate(Context context, String text) {
+ String evaledText = text;
+ if(context.isLogging()) logger.debug("{} - Evaluated {}=>{}", new Object[]{context, text, evaledText});
+ return evaledText;
+ }
+}
View
26 ants/src/ants/StringLiteral.java
@@ -0,0 +1,26 @@
+package ants;
+
+import ants.annotation.ConfigurableMethod;
+import ants.api.Configurable;
+import ants.api.Context;
+import ants.api.IString;
+
+public class StringLiteral extends Configurable
+ implements IString {
+
+ public StringLiteral(String tagName, String id) {
+ super(tagName, id);
+ }
+
+ private String value;
+
+ @ConfigurableMethod(required=true)
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue(Context context) {
+ return this.value;
+ }
+}
View
9 ants/src/ants/TaskManager.java
@@ -4,11 +4,18 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ants.api.Task;
+
/**
* Provides foundation for task execution
*/
public class TaskManager implements Task.AsyncMonitor {
+ static final Logger logger = LoggerFactory.getLogger(TaskManager.class);
+
static ThreadPoolExecutor cpuTaskExecutor;
static ThreadPoolExecutor syncIOTaskExecutor;
@@ -76,7 +83,7 @@ public void run() {
}
/**
- * @see ants.Task.AsyncMonitor#onAsyncDataReady(ants.Task)
+ * @see ants.api.Task.AsyncMonitor#onAsyncDataReady(ants.api.Task)
*/
@Override
public void onAsyncDataReady(final Task task) {
View
8 ants/src/ants/Util.java
@@ -4,16 +4,16 @@
public static String implode(Iterable<String> list, String seperator) {
StringBuffer value = new StringBuffer();
-
+
int i = 0;
- for(String string : list) {
- if(i > 0) {
+ for (String string : list) {
+ if (i > 0) {
value.append(seperator);
}
value.append(string);
i++;
}
-
+
return value.toString();
}
View
9 ants/src/ants/annotation/ConfigurableClass.java
@@ -0,0 +1,9 @@
+package ants.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConfigurableClass {
+ boolean expectsList() default false;
+}
View
14 ants/src/ants/annotation/ConfigurableMethod.java
@@ -0,0 +1,14 @@
+package ants.annotation;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface ConfigurableMethod {
+
+ boolean required() default false;
+ String defaultClass() default "";
+
+ String defaultListItemClass() default "";
+ String listItemTag() default "";
+}
View
21 ants/src/ants/api/Configurable.java
@@ -0,0 +1,21 @@
+package ants.api;
+
+
+public abstract class Configurable implements IConfigurable {
+
+ private String tagName;
+ private String id;
+
+ public Configurable(String tagName, String id) {
+ this.tagName = tagName;
+ this.id = id;
+ }
+
+ public String getTag() {
+ return this.tagName;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+}
View
40 ants/src/ants/api/Context.java
@@ -0,0 +1,40 @@
+package ants.api;
+
+public class Context {
+ private boolean logging;
+
+ private RequestContext requestContext;
+
+ private String iid;
+ private String moduleId;
+
+ public Context(RequestContext requestContext, String iid, String moduleId) {
+ this.requestContext = requestContext;
+
+ this.iid = iid;
+ this.moduleId = moduleId;
+
+ //TODO: Use request context to check filters
+ this.logging = true;
+ }
+
+ public RequestContext getRequestContext() {
+ return this.requestContext;
+ }
+
+ public String getInstanceId() {
+ return this.iid;
+ }
+
+ public String getModuleId() {
+ return this.moduleId;
+ }
+
+ public boolean isLogging() {
+ return this.logging;
+ }
+
+ public String toString() {
+ return "Context<" + this.moduleId + ", " + this.iid + ">";
+ }
+}
View
2 ants/src/ants/Data.java → ants/src/ants/api/Data.java
@@ -1,4 +1,4 @@
-package ants;
+package ants.api;
public class Data {
View
8 ants/src/ants/api/IConfigurable.java
@@ -0,0 +1,8 @@
+package ants.api;
+
+public interface IConfigurable {
+
+ String getTag();
+
+ String getId();
+}
View
8 ants/src/ants/api/IList.java
@@ -0,0 +1,8 @@
+package ants.api;
+
+import java.util.LinkedHashMap;
+
+public interface IList extends IConfigurable {
+
+ LinkedHashMap<String, IConfigurable> getItems(Context context);
+}
View
9 ants/src/ants/api/IParams.java
@@ -0,0 +1,9 @@
+package ants.api;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IParams extends IConfigurable {
+
+ List<Map.Entry<String, Object>> getPairs(Context context);
+}
View
7 ants/src/ants/api/IString.java
@@ -0,0 +1,7 @@
+package ants.api;
+
+
+public interface IString extends IConfigurable {
+
+ String getValue(Context context);
+}
View
69 ants/src/ants/api/ParamsDefault.java
@@ -0,0 +1,69 @@
+package ants.api;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import ants.StringDefault;
+
+@SiriObject(hasSetMethods=false)
+public class ParamsDefault extends Configurable
+ implements IParams {
+
+ static final Logger s_logger = LoggerFactory.getLogger(StringDefault.class);
+
+ ArrayList<IConfigurable> m_params;
+
+ public ParamsDefault() {
+ m_params = new ArrayList<IConfigurable>();
+ }
+
+ public void addParameter(IConfigurable a_param) {
+ m_params.add(a_param);
+ }
+
+ @Override
+ public Map<String, Object> getMap(Context a_context) {
+
+ TreeMap<String, Object> values = new TreeMap<String, Object>();
+
+ Iterator<IConfigurable> iter = m_params.iterator();
+ while(iter.hasNext()) {
+ IConfigurable param = iter.next();
+ if(param instanceof IString) {
+ IString istring = (IString)param;
+ String string = istring.get(a_context);
+ if(null == string) {
+ s_logger.error("{} - Failed to evaluate parameter: {} inside {}",
+ new Object[]{a_context, param, this});
+ return null;
+ }
+ values.put(param.getTag(), string);
+ } else if(param instanceof IParams) {
+ IParams iparams = (IParams)param;
+ Map<String, Object> childValues = iparams.getMap(a_context);
+ if(null == childValues) {
+ s_logger.error("{} - Failed to evaluate parameter: {} inside {}",
+ new Object[]{a_context, param, this});
+ return null;
+ }
+ values.put(param.getTag(), childValues);
+ }
+ else {
+ s_logger.error("{} - Invalid parameter type, string or map expected: {} inside {}",
+ new Object[]{a_context, param, this});
+ return null;
+ }
+ }
+
+ return values;
+ }
+
+ public String toDebugString() {
+ return m_params.toString();
+ }
+}
View
7 ants/src/ants/api/RequestContext.java
@@ -0,0 +1,7 @@
+package ants.api;
+
+public class RequestContext {
+
+ public RequestContext() {
+ }
+}
View
10 ants/src/ants/Task.java → ants/src/ants/api/Task.java
@@ -1,4 +1,4 @@
-package ants;
+package ants.api;
import java.util.LinkedList;
@@ -21,9 +21,13 @@
/**
* Callback to send the task completion notification to one
- * or more listeners
+ * or more listeners.
*/
public interface Callback {
+ /**
+ * The listener can return one more tasks as a result to execute
+ * after the callback
+ */
Iterable<Task> onComplete(Task task);
}
@@ -75,7 +79,7 @@ synchronized public void setData(Data data, Result result) {
public abstract Iterable<Task> run();
- Iterable<Callback> getCallbacks() {
+ public Iterable<Callback> getCallbacks() {
return this.callbacks;
}
View
12 ants/src/ants/exception/ObjectConfigureException.java
@@ -0,0 +1,12 @@
+package ants.exception;
+
+public class ObjectConfigureException extends Exception {
+
+ public ObjectConfigureException(String s) {
+ super(s);
+ }
+
+ public ObjectConfigureException(String s, Throwable cause) {
+ super(s, cause);
+ }
+}
View
8 ants/src/ants/exception/ObjectCreateException.java
@@ -0,0 +1,8 @@
+package ants.exception;
+
+public class ObjectCreateException extends Exception {
+
+ public ObjectCreateException(String s, Throwable cause) {
+ super(s, cause);
+ }
+}
View
8 ants/src/ants/exception/ObjectIncompleteException.java
@@ -0,0 +1,8 @@
+package ants.exception;
+
+public class ObjectIncompleteException extends Exception {
+
+ public ObjectIncompleteException(String s) {
+ super(s);
+ }
+}
View
12 ants/src/ants/exception/ObjectParseException.java
@@ -0,0 +1,12 @@
+package ants.exception;
+
+public class ObjectParseException extends Exception {
+
+ public ObjectParseException(String s) {
+ super(s);
+ }
+
+ public ObjectParseException(String s, Throwable cause) {
+ super(s, cause);
+ }
+}
View
251 ants/test/ants/test/ObjectFactoryTest.java
@@ -0,0 +1,251 @@
+package ants.test;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import junit.framework.TestListener;
+
+import org.junit.Test;
+
+import com.sun.corba.se.spi.orbutil.fsm.Input;
+
+import sun.reflect.ReflectionFactory.GetReflectionFactoryAction;
+import static org.junit.Assert.*;
+
+import ants.ObjectFactory;
+import ants.ObjectTree;
+import ants.StringDefault;
+import ants.annotation.ConfigurableClass;
+import ants.annotation.ConfigurableMethod;
+import ants.api.Configurable;
+import ants.api.IConfigurable;
+import ants.api.IList;
+import ants.exception.ObjectConfigureException;
+import ants.exception.ObjectCreateException;
+import ants.exception.ObjectIncompleteException;
+import ants.exception.ObjectParseException;
+
+public class ObjectFactoryTest {
+
+ @Test
+ public void testParse() {
+ ObjectFactory factory = new ObjectFactory();
+
+ boolean parseFailure = false;
+ try {
+ factory.parse("??");
+ } catch(ObjectParseException ex) {
+ parseFailure = true;
+ }
+ assertTrue("Invalid json parsing", parseFailure);
+
+ parseFailure = false;
+ try {
+ factory.parse("[]");
+ } catch(ObjectParseException ex) {
+ parseFailure = true;
+ }
+ assertFalse("Non object json parsing", parseFailure);
+
+ parseFailure = false;
+ try {
+ factory.parse("{}");
+ } catch(ObjectParseException ex) {
+ parseFailure = true;
+ }
+ assertFalse("object json parsing", parseFailure);
+ }
+
+ @ConfigurableClass(expectsList=true)
+ public static class TestList extends Configurable {
+
+ public TestList(String tagName, String id) {
+ super(tagName, id);
+ }
+
+ LinkedHashMap<String, IConfigurable> children;
+
+ LinkedHashMap<String, IConfigurable> getChildren() {
+ return this.children;
+ }
+
+ @ConfigurableMethod(listItemTag="child")
+ public void setList(LinkedHashMap<String, IConfigurable> children) {
+ this.children = children;
+ }
+ }
+
+ @ConfigurableClass(expectsList=true)
+ public static class TestParams extends Configurable {
+
+ public TestParams(String tagName, String id) {
+ super(tagName, id);
+ }
+
+ LinkedHashMap<String, IConfigurable> children;
+
+ LinkedHashMap<String, IConfigurable> getChildren() {
+ return this.children;
+ }
+
+ @ConfigurableMethod(listItemTag="param")
+ public void setList(LinkedHashMap<String, IConfigurable> children) {
+ this.children = children;
+ }
+ }
+
+ public static class TestClass extends Configurable {
+
+ public TestClass(String tagName, String id) {
+ super(tagName, id);
+ }
+
+ IConfigurable requiredChild;
+ IConfigurable notRequiredChild;
+ IConfigurable childObject;
+ IConfigurable childParams;
+ TestList childList;
+ TestParams childMap;
+
+ IConfigurable getRequiredChild() {
+ return this.requiredChild;
+ }
+
+ IConfigurable getNotRequiredChild() {
+ return this.notRequiredChild;
+ }
+
+ IConfigurable getChildObject() {
+ return this.childObject;
+ }
+
+ IConfigurable getChildParams() {
+ return this.childParams;
+ }
+
+ LinkedHashMap<String, IConfigurable> getChildList() {
+ return this.childList.getChildren();
+ }
+
+ LinkedHashMap<String, IConfigurable> getChildMap() {
+ return this.childMap.getChildren();
+ }
+
+ @ConfigurableMethod(required=true)
+ public void setRequiredChild(IConfigurable child) {
+ this.requiredChild = child;
+ }
+
+ public void setNotRequiredChild(IConfigurable child) {
+ this.notRequiredChild = child;
+ }
+
+ public void setChildObject(IConfigurable child) {
+ this.childObject = child;
+ }
+
+ public void setChildParams(IConfigurable child) {
+ this.childParams = child;
+ }
+
+ @ConfigurableMethod(defaultClass="ants.test.ObjectFactoryTest$TestList", listItemTag="item")
+ public void setChildList(IConfigurable list) {
+ this.childList = (TestList)list;
+ }
+
+ @ConfigurableMethod(defaultClass="ants.test.ObjectFactoryTest$TestParams")
+ public void setChildMap(IConfigurable map) {
+ this.childMap = (TestParams)map;
+ }
+ }
+
+ @Test
+ public void testChildren() throws ObjectParseException, ObjectCreateException, ObjectConfigureException, ObjectIncompleteException {
+ String jsonFile = "data/ObjectFactory/childObjectTree.json";
+ InputStream is = this.getClass().getResourceAsStream(jsonFile);
+ ObjectFactory factory = new ObjectFactory();
+ ObjectTree tree = factory.parse(jsonFile, is);
+
+ TestClass testObject = (TestClass)factory.configure(tree, TestClass.class.getName(), "test", "t1", "", "");
+
+ assertEquals("Tag is populated", "test", testObject.getTag());
+ assertEquals("Id is populated", "t1", testObject.getId());
+ assertEquals("Required child should be a string", StringDefault.class, testObject.getRequiredChild().getClass());
+ assertEquals("Not required child should be a string", StringDefault.class, testObject.getNotRequiredChild().getClass());
+ assertEquals("Child object should be of type TestClass", TestClass.class, testObject.getChildObject().getClass());
+ assertEquals("Child list should be configured", 2, testObject.getChildList().size());
+ assertEquals("Child map should be configured", 2, testObject.getChildMap().size());
+
+ // test params
+
+ }
+
+ @Test
+ public void testMissingChildren() throws ObjectParseException, ObjectCreateException, ObjectConfigureException {
+ ObjectFactory factory = new ObjectFactory();
+ ObjectTree tree = factory.parse("{}");
+
+ boolean incomplete = false;
+ try {
+ factory.configure(tree, TestClass.class.getName(), "test", "t1", "", "");
+ } catch (ObjectConfigureException e) {
+ incomplete = true;
+ }
+ assertTrue("Incomplete object cannot be configured", incomplete);
+ }
+
+ @Test
+ public void testParamsArray() throws ObjectParseException, ObjectCreateException, ObjectConfigureException, ObjectIncompleteException {
+ String jsonFile = "data/ObjectFactory/paramsArrayTree.json";
+ InputStream is = this.getClass().getResourceAsStream(jsonFile);
+ ObjectFactory factory = new ObjectFactory();
+ ObjectTree tree = factory.parse(jsonFile, is);
+
+ TestList testList = (TestList)factory.configure(tree, TestList.class.getName(), "params", "", "", "child");
+
+ LinkedHashMap<String, IConfigurable> children = testList.getChildren();
+ assertEquals("Params have a list of children", 2, children.size());
+ Iterator<IConfigurable> collection = children.values().iterator();
+
+ IConfigurable first = collection.next();
+ IConfigurable second = collection.next();
+
+ assertEquals("First children is a string", StringDefault.class, first.getClass());
+ assertEquals("Child tag is set", "child", first.getTag());
+ assertTrue("First children has auto id", !first.getId().isEmpty());
+ assertEquals("Child tag is set", "child", second.getTag());
+ assertEquals("Second children has id", "i2", second.getId());
+ }
+
+ @Test
+ public void testParamsMap() throws ObjectParseException, ObjectCreateException, ObjectConfigureException, ObjectIncompleteException {
+ String jsonFile = "data/ObjectFactory/paramsMapTree.json";
+ InputStream is = this.getClass().getResourceAsStream(jsonFile);
+ ObjectFactory factory = new ObjectFactory();
+ ObjectTree tree = factory.parse(jsonFile, is);
+
+ TestParams testParams = (TestParams)factory.configure(tree, TestParams.class.getName(), "params", "", "", "testParam");
+
+ LinkedHashMap<String, IConfigurable> children = testParams.getChildren();
+ assertEquals("Params have a list of children", 2, children.size());
+ Iterator<IConfigurable> collection = children.values().iterator();
+
+ IConfigurable first = collection.next();
+ IConfigurable second = collection.next();
+
+ assertEquals("Child tag is set", "testParam", first.getTag());
+ assertEquals("Child tag is set", "testParam", second.getTag());
+ assertEquals("First child is object", TestClass.class, first.getClass());
+ assertEquals("Second child is object", TestClass.class, second.getClass());
+
+ HashSet<String> idSet = new HashSet<String>();
+ idSet.add("i1");
+ idSet.add("i2");
+ assertTrue("All ids are set", children.keySet().equals(idSet));
+ }
+
+}
View
17 ants/test/ants/test/data/ObjectFactory/childObjectTree.json
@@ -0,0 +1,17 @@
+
+{
+ "requiredChild": "",
+ "notRequiredChild": "",
+ "childObject": {
+ "@class": "ants.test.ObjectFactoryTest$TestClass",
+ "requiredChild": ""
+ },
+ "childList": [
+ "i1",
+ "i2"
+ ],
+ "childMap": {
+ "i1": "v1",
+ "i2": "v2"
+ }
+}
View
8 ants/test/ants/test/data/ObjectFactory/paramsArrayTree.json
@@ -0,0 +1,8 @@
+[
+ "string",
+ {
+ "@class": "ants.test.ObjectFactoryTest$TestClass",
+ "@id": "i2",
+ "requiredChild": ""
+ }
+]
View
10 ants/test/ants/test/data/ObjectFactory/paramsMapTree.json
@@ -0,0 +1,10 @@
+{
+ "i1": {
+ "@class": "ants.test.ObjectFactoryTest$TestClass",
+ "requiredChild": ""
+ },
+ "i2": {
+ "@class": "ants.test.ObjectFactoryTest$TestClass",
+ "requiredChild": ""
+ }
+}

0 comments on commit b166bea

Please sign in to comment.