Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json aliases feature #247

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ target
.project
.settings
.springBeans
*.iml
Original file line number Diff line number Diff line change
@@ -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<String, JsonObjectNode> extractYogaAlias(JsonParser jsonParser) throws IOException {
jsonParser.nextToken();
JsonObjectNode objectNode = null;
Map<String, JsonObjectNode> entities = new HashMap<String, JsonObjectNode>();
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);
}

}

Original file line number Diff line number Diff line change
@@ -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<String, String> aliases;

public AliasJsonSelectorResolver() throws IOException, URISyntaxException {
aliases = compute();
}

@Override
public String resolveSelector(String aliasSelectorExpression) throws ParseSelectorException {
return aliases.get(aliasSelectorExpression);
}

private Map<String, String> compute() throws IOException, URISyntaxException {
AliasJsonSelectorParser parser = new AliasJsonSelectorParser();
String[] fileList = new File(getClass().getResource(DIRECTORY).toURI()).list();
Map<String, JsonObjectNode> entities = new HashMap<String, JsonObjectNode>();
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<String, String> aliases = new HashMap<String, String>();
for(JsonObjectNode objectNode : entities.values()) {
objectNode.compute(entities);
aliases.put(objectNode.getName(), objectNode.toString());
}
return aliases;
}

}
Original file line number Diff line number Diff line change
@@ -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<JsonPropertyNode> children = new ArrayList<JsonPropertyNode>();
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<String, JsonObjectNode> entities) {
for(JsonPropertyNode property : children) {
property.compute(entities);
}
}

public Boolean isAllChildrenComputed() {
return allChildrenComputed;
}

public void setAllChildrenComputed(Boolean allChildrenComputed) {
this.allChildrenComputed = allChildrenComputed;
}

}
Original file line number Diff line number Diff line change
@@ -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<String, JsonObjectNode> 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();
}

}

Original file line number Diff line number Diff line change
@@ -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<String, JsonObjectNode> 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<String, JsonObjectNode> 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<String, JsonObjectNode> 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<String, JsonObjectNode> 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<String, JsonObjectNode> 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());
}

}

Original file line number Diff line number Diff line change
@@ -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);
}

}

Empty file.
15 changes: 15 additions & 0 deletions yoga-core/src/test/resources/yoga_aliases/variables.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"@variable": [
"test",
"test2"
],
"$variable2": [
"testvar",
{
"childvar": [
"child",
"@variable2"
]
}
]
}
Loading