diff --git a/README.md b/README.md index 4e193f7..0e06d09 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,18 @@ title: title|name description: descriptions/description[lang=en,es] ``` +### ENUM Maps + +### HashMap Values + +You may want to extract more complicated values out of your documents. You may construct sub-HashMaps + +```$xslt +sub_mapping: + - name: value + +``` + ## Comparison ### Serializing XML in Java diff --git a/src/main/java/com/hulu/ftl/FTLDefinition.java b/src/main/java/com/hulu/ftl/FTLDefinition.java index a4ecb89..39e51e4 100644 --- a/src/main/java/com/hulu/ftl/FTLDefinition.java +++ b/src/main/java/com/hulu/ftl/FTLDefinition.java @@ -18,7 +18,7 @@ public class FTLDefinition { - ArrayList fields = new ArrayList<>(); + public ArrayList fields = new ArrayList<>(); public FTLDefinition(String configFTL) { diff --git a/src/main/java/com/hulu/ftl/FTLField.java b/src/main/java/com/hulu/ftl/FTLField.java index d864076..c3c66d3 100644 --- a/src/main/java/com/hulu/ftl/FTLField.java +++ b/src/main/java/com/hulu/ftl/FTLField.java @@ -1,10 +1,9 @@ package com.hulu.ftl; import com.hulu.ftl.annotations.Annotation; +import com.hulu.ftl.annotations.Literal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; +import java.util.*; public class FTLField { public String key; @@ -14,19 +13,48 @@ public class FTLField { public ArrayList subSelectors = new ArrayList<>(); public Boolean isMultiValue = false; + public Boolean isRootRelative = false; + + public FTLField(String key, Annotation selector) { + construct(key, (Annotation) selector); + } public FTLField(String key, Object selector) { switch(selector.getClass().getSimpleName()) { case "String": construct(key, (String) selector); break; case "LinkedHashMap": - construct(key, (LinkedHashMap) selector); + // Needs to be cleaned up + LinkedHashMap sel = (LinkedHashMap) selector; + Object firstValue = sel.entrySet().iterator().next().getValue(); + + + if (firstValue != null) { + switch(firstValue.getClass().getSimpleName()) { + case "Literal": + case "String": + construct(key); + isRootRelative = true; + isMultiValue = true; + for(Map.Entry entry : sel.entrySet()) { + subSelectors.add( + new FTLField(entry.getKey(), entry.getValue()) + ); + } + + break; + default: construct(key, sel); + } + } + break; case "Literal": case "Template": case "Mapping": construct(key, (Annotation) selector); break; + default: + System.out.println("UT O, we didn't do:" + selector.getClass().getSimpleName()); } } @@ -36,8 +64,19 @@ public void construct(String key, Annotation value) { annotation = value; } + public void construct(String key) { + this.key = key; + selectors = new String[0]; // shouldn't have to do this everywhere. + isRootRelative = true; + } + public void construct(String key, String selector) { this.key = key; + // if selector starts with `/`, it will be relative to the root document node. + if(selector.startsWith("/")) { + isRootRelative = true; + selector = selector.substring(1); + } // if the selector ends with `*`, it will return multiple values. if(selector.endsWith("*")) { @@ -57,6 +96,7 @@ public void construct(String key, String selector) { public void construct(String key, LinkedHashMap selector) { this.key = key; + // bad selectors = Arrays.copyOf(selector.keySet().toArray(), selector.values().size(), String[].class); for(int x=0; x selector) { } for(String sel : selectors) { - LinkedHashMap subValues = (LinkedHashMap) selector.get(sel); + if(selector.get(sel) instanceof LinkedHashMap) { // this turnary is horrible, extract to map + LinkedHashMap subValues = (LinkedHashMap) selector.get(sel); - subValues.forEach((k, val) -> { - // meh? .replaceAll("[*]$", "") - if (val instanceof String) - subSelectors.add(new FTLField(k, ((String)val))); - else + subValues.forEach((k, val) -> { + // meh? .replaceAll("[*]$", "") subSelectors.add(new FTLField(k, val)); - }); + }); + + } else if(selector.get(sel) instanceof String) { + //Literal + //String + //WTF? + subSelectors.add(new FTLField("yuck", selector.get(sel))); + } +// else if(selector.get(sel) instanceof List) { // HERE, not LinkedHashMap, then what? +// List subValues = (List) selector.get(sel); +// +// subValues.forEach(hash -> { +// subSelectors.add(new FTLField(hash)) +// }); +// } } } @@ -88,4 +140,14 @@ public Boolean hasSubFields() { return subSelectors.size() > 0; } + public String toString() { + String repr = ""; + + for(FTLField sub : subSelectors) { + repr += "\n " + subSelectors.toString(); + } + + return repr; + } + } diff --git a/src/main/java/com/hulu/ftl/annotations/Annotation.java b/src/main/java/com/hulu/ftl/annotations/Annotation.java index 6f3cd99..1b8c1a4 100644 --- a/src/main/java/com/hulu/ftl/annotations/Annotation.java +++ b/src/main/java/com/hulu/ftl/annotations/Annotation.java @@ -3,7 +3,7 @@ import java.util.Map; public abstract class Annotation { - Object value; + public Object value; public Object getValue(Map localContext) { return value.toString(); diff --git a/src/main/java/com/hulu/ftl/formats/XMLFormat.java b/src/main/java/com/hulu/ftl/formats/XMLFormat.java index 3efa207..2d01a2a 100644 --- a/src/main/java/com/hulu/ftl/formats/XMLFormat.java +++ b/src/main/java/com/hulu/ftl/formats/XMLFormat.java @@ -41,7 +41,6 @@ public ArrayList findNodes(String[] selectors) { rootSelectors[x] = rootSelectors[x].equals("/") ? "/" : "/*/" + rootSelectors[x]; } - return findNodes(rootSelectors, document); } @@ -53,17 +52,16 @@ public ArrayList findNodes(String[] selectors, Node document) { nodes.addAll(nodeArray(selector, document)); - } return nodes; } - public ArrayList getBySelector(String selector) { + private ArrayList getBySelector(String selector) { return getBySelector(selector, nodeArray("/*", document)); } - public ArrayList getBySelector(String selector, List rootNodes) { + private ArrayList getBySelector(String selector, List rootNodes) { ArrayList values = new ArrayList<>(); try { @@ -103,6 +101,10 @@ private ArrayList nodeArray(String selector, Node rootNode) { return list; } + /* + * Run-Time deserializing of document + */ + @Override public Object getValue(FTLField field) { List values = getValues(field); @@ -131,11 +133,18 @@ public List getValues(FTLField field, List rootNodes) { if(field.hasSubFields()) { List list = new ArrayList<>(); + if( rootNodes.size() == 0) { + rootNodes.add(document); + } + for(Node rootNode : rootNodes) { HashMap subMap = new HashMap<>(); int normalValueNum = 0; for(FTLField subField : field.subSelectors) { + if(subField.isRootRelative) { + rootNode = document; + } ArrayList nodes = findNodes(subField.selectors, rootNode); @@ -155,7 +164,7 @@ public List getValues(FTLField field, List rootNodes) { subMap.put(subField.key, field.annotation); } } - if (normalValueNum > 0) + if (normalValueNum > 0 || list.size() == 0) list.add(subMap); } @@ -168,13 +177,20 @@ public List getValues(FTLField field, List rootNodes) { // Not found, look for attr for(String selector : field.selectors) { + List originNodes = rootNodes; + + if(field.isRootRelative) { + originNodes = new ArrayList(); + originNodes.add(document); + } + String[] elements = selector.split("/"); if( elements.length > 0) { Integer end = elements.length > 0 ? elements.length - 1 : 0; String attrSelector = Arrays.stream( - Arrays.copyOfRange(elements, 0, end) + Arrays.copyOfRange(elements, 0, end) ).collect(Collectors.joining("/")); if(end != 0) { @@ -185,11 +201,11 @@ public List getValues(FTLField field, List rootNodes) { values.addAll(getBySelector(attrSelector)); if (values.size() == 0) - values.addAll(getBySelector(attrSelector, rootNodes)); + values.addAll(getBySelector(attrSelector, originNodes)); if (values.size() == 0) - values.addAll(getBySelector(selector + "/text()", rootNodes)); + values.addAll(getBySelector(selector + "/text()", originNodes)); if (values.size() == 0) - values.addAll(getBySelector("./text()", rootNodes)); + values.addAll(getBySelector("./text()", originNodes)); if(values.size() > 0) { diff --git a/src/main/resources/external_ids.ftl b/src/main/resources/external_ids.ftl new file mode 100644 index 0000000..3f0a612 --- /dev/null +++ b/src/main/resources/external_ids.ftl @@ -0,0 +1,5 @@ +external_ids: + - namespace: !lit tms + id: /TMSId + - namespace: !lit gracenote_episode + id: /rootId \ No newline at end of file diff --git a/src/main/resources/external_ids_legacy.ftl b/src/main/resources/external_ids_legacy.ftl new file mode 100644 index 0000000..a5eef67 --- /dev/null +++ b/src/main/resources/external_ids_legacy.ftl @@ -0,0 +1,7 @@ +external_ids: + - /*: + namespace: !lit tms + id: TMSId + - /*: + namespace: !lit gracenote_episode + id: rootId \ No newline at end of file diff --git a/src/test/java/com/hulu/ftl/FtlFieldTests.java b/src/test/java/com/hulu/ftl/FtlFieldTests.java new file mode 100644 index 0000000..81dbf50 --- /dev/null +++ b/src/test/java/com/hulu/ftl/FtlFieldTests.java @@ -0,0 +1,56 @@ +package com.hulu.ftl; + +import com.hulu.ftl.exceptions.FTLNotImplemented; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +public class FtlFieldTests { + Map document; + + @Before + public void setup() throws IOException, FTLNotImplemented { + FTLDefinition definition = new FTLDefinition("external_ids.ftl"); + document = definition.parse("program.xml"); + } + + @Test + public void testSubList() { + List exids = (List) document.get("external_ids"); + + assertEquals(2, exids.size()); + } + + @Test + public void relativeRootValue() { + FTLField field = new FTLField("name", "/rootId"); + assertTrue(field.isRootRelative); + assertEquals(field.selectors[0], "rootId"); + } + + @Test + public void findsExternalIds() { + + } + + @Test + public void findsHashTransformValues() { + List externalIds = (List) document.get("external_ids"); + + HashMap externalId = + (HashMap) externalIds.get(0); + + assertEquals(externalId.get("namespace"), "tms"); + assertEquals(externalId.get("id"), "MV000975940000"); + + } +} diff --git a/src/test/java/com/hulu/ftl/JSONTests.java b/src/test/java/com/hulu/ftl/JSONTests.java index 411f5ed..cb0c3ee 100644 --- a/src/test/java/com/hulu/ftl/JSONTests.java +++ b/src/test/java/com/hulu/ftl/JSONTests.java @@ -15,7 +15,6 @@ public class JSONTests { public void initialize() throws Exception { FTLDefinition definition = new FTLDefinition("jsontest.ftl"); program = definition.parse(new File("program.json")); - System.out.println(program); } @Test diff --git a/src/test/java/com/hulu/ftl/ProgramTests.java b/src/test/java/com/hulu/ftl/ProgramTests.java index acb13ae..79ce0c1 100644 --- a/src/test/java/com/hulu/ftl/ProgramTests.java +++ b/src/test/java/com/hulu/ftl/ProgramTests.java @@ -5,6 +5,8 @@ import java.io.File; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; @@ -61,4 +63,33 @@ public void mapping() { assertEquals("episode", type); } + @Test + public void findsHashTransforms() { + List externalIds = (List) program.get("external_ids"); + + assertEquals(externalIds.size(), 2); + } + + @Test + public void findsHashTransformValues() { + List externalIds = (List) program.get("external_ids"); + + HashMap externalId = + (HashMap) externalIds.get(0); + + assertEquals(externalId.get("namespace"), "tms"); + assertEquals(externalId.get("id"), "EP000000060001"); + } + + @Test + public void rootRelativeValue() { + + } + + @Test + public void rootRelativeSubValue() { + + } + + // external ids } diff --git a/src/test/java/com/hulu/ftl/definitions/HashDefinitionTests.java b/src/test/java/com/hulu/ftl/definitions/HashDefinitionTests.java new file mode 100644 index 0000000..2207428 --- /dev/null +++ b/src/test/java/com/hulu/ftl/definitions/HashDefinitionTests.java @@ -0,0 +1,47 @@ +package com.hulu.ftl.definitions; + +import com.hulu.ftl.FTLDefinition; +import com.hulu.ftl.FTLField; +import com.hulu.ftl.annotations.Literal; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +public class HashDefinitionTests { + FTLDefinition definition; + + @Before + public void setup() { + definition = new FTLDefinition("external_ids_legacy.ftl"); + } + + @Test + public void findsParentList() { + FTLField field = definition.fields.get(0); + + assertEquals(field.key, "external_ids"); + assertEquals(field.subSelectors.size(), 2); + } + + @Test + public void findsListValues() { + FTLField field0 = definition.fields.get(0).subSelectors.get(0); + FTLField field1 = definition.fields.get(0).subSelectors.get(1); + + FTLField field2 = definition.fields.get(1).subSelectors.get(0); + + String[] tmsSelector = {}; + + assertEquals(field0.key, "namespace"); + + assertEquals(field1.key, "id"); + + Literal lit = (Literal) field2.annotation; + assertEquals("namespace", field2.key); + assertEquals("gracenote_episode", lit.value); + assertEquals(tmsSelector, field2.selectors); +// assertEquals(field.subSelectors.size(), 2); + } +} diff --git a/src/test/java/com/hulu/ftl/definitions/ListDefinitionTests.java b/src/test/java/com/hulu/ftl/definitions/ListDefinitionTests.java new file mode 100644 index 0000000..a1a60d6 --- /dev/null +++ b/src/test/java/com/hulu/ftl/definitions/ListDefinitionTests.java @@ -0,0 +1,49 @@ +package com.hulu.ftl.definitions; + +import com.hulu.ftl.FTLDefinition; +import com.hulu.ftl.FTLField; +import com.hulu.ftl.annotations.Literal; +import com.hulu.ftl.exceptions.FTLNotImplemented; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +public class ListDefinitionTests { + FTLDefinition definition; + + @Before + public void setup() throws IOException, FTLNotImplemented { + definition = new FTLDefinition("external_ids.ftl"); + } + + @Test + public void findsParentList() { + FTLField field = definition.fields.get(0); + + assertEquals(field.key, "external_ids"); + //assertEquals(field.selectors, null); + assertEquals(field.subSelectors.size(), 2); + } + + @Test + public void findsListValues() { + FTLField field0 = definition.fields.get(0).subSelectors.get(0); + + String[] tmsSelector = {}; + + assertEquals("namespace", field0.key); + assertEquals(tmsSelector, field0.selectors); + + Literal lit = (Literal) field0.annotation; + assertEquals("namespace", field0.key); + assertEquals("tms", lit.value); + } +}