diff --git a/.gitignore b/.gitignore index 3337b55e..f1ca7f30 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ target .project .settings .springBeans +*.iml \ No newline at end of file diff --git a/yoga-core/src/main/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorParser.java b/yoga-core/src/main/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorParser.java new file mode 100644 index 00000000..711faf9a --- /dev/null +++ b/yoga-core/src/main/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorParser.java @@ -0,0 +1,62 @@ +package org.skyscreamer.yoga.selector.parser; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import org.skyscreamer.yoga.util.JsonObjectNode; +import org.skyscreamer.yoga.util.JsonPropertyNode; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class AliasJsonSelectorParser { + + private static final String JSON_NODE_TYPE_VALUE_STRING = JsonToken.VALUE_STRING.name(); + private static final String JSON_NODE_TYPE_END_ARRAY = JsonToken.END_ARRAY.name(); + private static final String JSON_NODE_TYPE_FIELD_NAME = JsonToken.FIELD_NAME.name(); + + public Map extractYogaAlias(JsonParser jsonParser) throws IOException { + jsonParser.nextToken(); + JsonObjectNode objectNode = null; + Map entities = new HashMap(); + while(jsonParser.hasCurrentToken()) { + String nodeType = jsonParser.getCurrentToken().name(); + if(enterInObject(nodeType)) { + String name = jsonParser.getCurrentName(); + objectNode = new JsonObjectNode(name, objectNode); + } else if(isAllChildrenComputed(nodeType)) { + if(objectNode != null && objectNode.isRootNode()) { + objectNode.setAllChildrenComputed(true); + if(objectNode.isValid()) { + entities.put(objectNode.getName(), objectNode); + } else { + throw new RuntimeException("The yoga alias '"+objectNode.getName()+" must start by "+JsonPropertyNode.YOGA_SYMBOL_ALIAS+" (or "+JsonPropertyNode.YOGA_SYMBOL_VARIABLE+" for a variable) !!"); + } + objectNode = null; + } else if(objectNode != null) { + objectNode = objectNode.getParentNode(); + } + } else if(isAValueProperty(nodeType)) { + String value = jsonParser.getValueAsString(); + JsonPropertyNode property = new JsonPropertyNode(value); + objectNode.addChild(property); + } + jsonParser.nextToken(); + } + return entities; + } + + private boolean isAValueProperty(String nodeType) { + return JSON_NODE_TYPE_VALUE_STRING.equals(nodeType); + } + + private boolean isAllChildrenComputed(String nodeType) { + return JSON_NODE_TYPE_END_ARRAY.equals(nodeType); + } + + private boolean enterInObject(String nodeType) { + return JSON_NODE_TYPE_FIELD_NAME.equals(nodeType); + } + +} + diff --git a/yoga-core/src/main/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorResolver.java b/yoga-core/src/main/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorResolver.java new file mode 100644 index 00000000..7a10bdc2 --- /dev/null +++ b/yoga-core/src/main/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorResolver.java @@ -0,0 +1,51 @@ +package org.skyscreamer.yoga.selector.parser; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import org.skyscreamer.yoga.exceptions.ParseSelectorException; +import org.skyscreamer.yoga.util.JsonObjectNode; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +public class AliasJsonSelectorResolver implements AliasSelectorResolver { + + private final static String DIRECTORY = "/yoga_aliases"; + private final static String PATTERN_END_FILE = ".json"; + + private Map aliases; + + public AliasJsonSelectorResolver() throws IOException, URISyntaxException { + aliases = compute(); + } + + @Override + public String resolveSelector(String aliasSelectorExpression) throws ParseSelectorException { + return aliases.get(aliasSelectorExpression); + } + + private Map compute() throws IOException, URISyntaxException { + AliasJsonSelectorParser parser = new AliasJsonSelectorParser(); + String[] fileList = new File(getClass().getResource(DIRECTORY).toURI()).list(); + Map entities = new HashMap(); + for(String fileName : fileList) { + if(fileName.endsWith(PATTERN_END_FILE)) { + JsonParser jsonParser = new JsonFactory().createParser(new File(getClass().getResource(DIRECTORY + "/" + fileName).toURI())); + entities.putAll(parser.extractYogaAlias(jsonParser)); + } else { + Logger.getLogger("AliasJsonSelectorResolver").warning("The filename " + fileName + " not end with " + PATTERN_END_FILE + " : it's unused!"); + } + } + Map aliases = new HashMap(); + for(JsonObjectNode objectNode : entities.values()) { + objectNode.compute(entities); + aliases.put(objectNode.getName(), objectNode.toString()); + } + return aliases; + } + +} diff --git a/yoga-core/src/main/java/org/skyscreamer/yoga/util/JsonObjectNode.java b/yoga-core/src/main/java/org/skyscreamer/yoga/util/JsonObjectNode.java new file mode 100644 index 00000000..6043bc8d --- /dev/null +++ b/yoga-core/src/main/java/org/skyscreamer/yoga/util/JsonObjectNode.java @@ -0,0 +1,64 @@ +package org.skyscreamer.yoga.util; + +import org.apache.commons.lang.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class JsonObjectNode extends JsonPropertyNode { + + private JsonObjectNode parentNode; + private List children = new ArrayList(); + private Boolean allChildrenComputed = false; + + public JsonObjectNode(String name, JsonObjectNode parentNode) { + super(name); + this.parentNode = parentNode; + if(parentNode != null) { + parentNode.addChild(this); + } + } + + public Boolean isRootNode() { + return this.parentNode == null; + } + + public JsonObjectNode addChild(JsonPropertyNode child) { + children.add(child); + return this; + } + + public JsonObjectNode getParentNode() { + return this.parentNode; + } + + @Override + public String toString() { + StringBuilder res = new StringBuilder(); + if(!this.isRootNode()) { + res.append(this.name).append("("); + } + res.append(StringUtils.join(children, ",")); + if(!this.isRootNode()) { + res.append(")"); + } + return res.toString(); + } + + @Override + public void compute(Map entities) { + for(JsonPropertyNode property : children) { + property.compute(entities); + } + } + + public Boolean isAllChildrenComputed() { + return allChildrenComputed; + } + + public void setAllChildrenComputed(Boolean allChildrenComputed) { + this.allChildrenComputed = allChildrenComputed; + } + +} diff --git a/yoga-core/src/main/java/org/skyscreamer/yoga/util/JsonPropertyNode.java b/yoga-core/src/main/java/org/skyscreamer/yoga/util/JsonPropertyNode.java new file mode 100644 index 00000000..d95e220d --- /dev/null +++ b/yoga-core/src/main/java/org/skyscreamer/yoga/util/JsonPropertyNode.java @@ -0,0 +1,59 @@ +package org.skyscreamer.yoga.util; + +import java.util.Map; + +public class JsonPropertyNode { + + public static final String YOGA_SYMBOL_ALIAS = "$"; + public static final String YOGA_SYMBOL_VARIABLE = "@"; + + protected String name; + private String resolvedValue; + + public JsonPropertyNode(String name) { + this.name = name; + } + + public Boolean isAlias() { + return this.name.startsWith(YOGA_SYMBOL_ALIAS); + } + + public Boolean isVariable() { + return this.name.startsWith(YOGA_SYMBOL_VARIABLE); + } + + public boolean isValid() { + return isAlias() || isVariable(); + } + + public String getName() { + return this.name; + } + + @Override + public String toString() { + if(this.isVariable()) { + if(this.resolvedValue == null) { + throw new RuntimeException("The variable " + this.name + " has not corresponding value !"); + } else { + return this.resolvedValue; + } + } + return this.name; + } + + public void compute(Map entities) { + if(isVariable()) { + resolve(entities.get(getName())); + } + } + + private void resolve(JsonObjectNode objectNode) { + if(objectNode == null) { + throw new RuntimeException("The variable " + this.name + " has not corresponding value !"); + } + this.resolvedValue = objectNode.toString(); + } + +} + diff --git a/yoga-core/src/test/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorParserTest.java b/yoga-core/src/test/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorParserTest.java new file mode 100644 index 00000000..04645f89 --- /dev/null +++ b/yoga-core/src/test/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorParserTest.java @@ -0,0 +1,74 @@ +package org.skyscreamer.yoga.selector.parser; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonFactory; +import org.skyscreamer.yoga.util.JsonObjectNode; + +public class AliasJsonSelectorParserTest { + + private AliasJsonSelectorParser parser; + + @Before + public void setUp() { + parser = new AliasJsonSelectorParser(); + } + + @Test + public void should_transform_simple_json_into_alias() throws Exception { + Map res = parser.extractYogaAlias(new JsonFactory().createParser("{\"$simple\":[\"test\"]}")); + assertNotNull(res.get("$simple")); + assertEquals(true, res.get("$simple").isValid()); + assertEquals(true, res.get("$simple").isAlias()); + assertEquals("test", res.get("$simple").toString()); + } + + @Test + public void should_transform_simple_json_into_variable() throws Exception { + Map res = parser.extractYogaAlias(new JsonFactory().createParser("{\"@simple\":[\"test\"]}")); + assertNotNull(res.get("@simple")); + assertEquals(true, res.get("@simple").isValid()); + assertEquals(true, res.get("@simple").isVariable()); + assertEquals("test", res.get("@simple").toString()); + } + + @Test(expected=RuntimeException.class) + public void should_throw_exception_if_type_is_not_valid() throws Exception { + parser.extractYogaAlias(new JsonFactory().createParser("{\"simple\":[\"test\"]}")); + } + + @Test + public void should_transform_children_json() throws Exception { + Map res = parser.extractYogaAlias(new JsonFactory().createParser("{\"@simple\":[{\"test\": [\"test\"]}, \"test\"]}")); + assertNotNull(res.get("@simple")); + assertEquals(true, res.get("@simple").isValid()); + assertEquals(true, res.get("@simple").isVariable()); + assertEquals("test(test),test", res.get("@simple").toString()); + } + + @Test + public void should_transform_complexe_children_json() throws Exception { + Map res = parser.extractYogaAlias(new JsonFactory().createParser("{\"@simple\":[{\"test\": [\"test\", {\"test2\": [\"test2\"]}]}, \"test\"]}")); + assertNotNull(res.get("@simple")); + assertEquals(true, res.get("@simple").isValid()); + assertEquals(true, res.get("@simple").isVariable()); + assertEquals("test(test,test2(test2)),test", res.get("@simple").toString()); + } + + @Test + public void should_transform_2_brothers() throws Exception { + Map res = parser.extractYogaAlias(new JsonFactory().createParser("{\"@simple\":[{\"test\": [{\"test2\": [\"test2\"], \"test3\": [\"test3\"]}]}, \"test\"]}")); + assertNotNull(res.get("@simple")); + assertEquals(true, res.get("@simple").isValid()); + assertEquals(true, res.get("@simple").isVariable()); + assertEquals("test(test2(test2),test3(test3)),test", res.get("@simple").toString()); + } + +} + diff --git a/yoga-core/src/test/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorResolverTest.java b/yoga-core/src/test/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorResolverTest.java new file mode 100644 index 00000000..886ffe95 --- /dev/null +++ b/yoga-core/src/test/java/org/skyscreamer/yoga/selector/parser/AliasJsonSelectorResolverTest.java @@ -0,0 +1,51 @@ +package org.skyscreamer.yoga.selector.parser; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.net.URISyntaxException; + +import static org.junit.Assert.assertEquals; + +public class AliasJsonSelectorResolverTest { + + private AliasJsonSelectorResolver resolver; + + @Before + public void setUp() throws IOException, URISyntaxException { + resolver = new AliasJsonSelectorResolver(); + } + + @Test + public void should_transform_simple_json_into_string() throws Exception { + String res = resolver.resolveSelector("$simple"); + assertEquals("test", res); + } + + @Test + public void should_transform_json_with_child_into_string() throws Exception { + String res = resolver.resolveSelector("$child"); + assertEquals("test,child(value)", res); + } + + @Test + public void should_transform_json_with_children_into_string() throws Exception { + String res = resolver.resolveSelector("$children"); + assertEquals("test,child(value,value2),child2(value3,child3(*))", res); + } + + @Test + public void should_transform_json_with_variables() throws Exception { + String res = resolver.resolveSelector("$variable"); + assertEquals("test,test2", res); + } + + @Test + public void should_transform_complexe_json_with_variables() throws Exception { + String res = resolver.resolveSelector("$variable2"); + assertEquals("testvar,childvar(child,test,child(value,value2),child2(value3,child3(*)))", res); + } + +} + diff --git a/yoga-core/src/test/resources/yoga_aliases/no_an_alias b/yoga-core/src/test/resources/yoga_aliases/no_an_alias new file mode 100644 index 00000000..e69de29b diff --git a/yoga-core/src/test/resources/yoga_aliases/variables.json b/yoga-core/src/test/resources/yoga_aliases/variables.json new file mode 100644 index 00000000..68241400 --- /dev/null +++ b/yoga-core/src/test/resources/yoga_aliases/variables.json @@ -0,0 +1,15 @@ +{ + "@variable": [ + "test", + "test2" + ], + "$variable2": [ + "testvar", + { + "childvar": [ + "child", + "@variable2" + ] + } + ] +} \ No newline at end of file diff --git a/yoga-core/src/test/resources/yoga_aliases/yoga-aliases.json b/yoga-core/src/test/resources/yoga_aliases/yoga-aliases.json new file mode 100644 index 00000000..4d3ef464 --- /dev/null +++ b/yoga-core/src/test/resources/yoga_aliases/yoga-aliases.json @@ -0,0 +1,48 @@ +{ + "$simple": [ + "test" + ], + "$child": [ + "test", + { + "child": ["value"] + } + ], + "$children": [ + "test", + { + "child" : [ + "value", + "value2" + ] + }, + { + "child2": [ + "value3", + { + "child3": ["*"] + } + ] + } + ], + "$variable": [ + "@variable" + ], + "@variable2": [ + "test", + { + "child" : [ + "value", + "value2" + ] + }, + { + "child2": [ + "value3", + { + "child3": ["*"] + } + ] + } + ] +} \ No newline at end of file