Last active
February 16, 2017 17:50
-
-
Save ejwinter/23b94013d956c680afe69664496cdddb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package edu.mayo.cim.mxd.fileloader; | |
| import com.google.common.base.Splitter; | |
| import com.google.common.collect.*; | |
| import java.util.*; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| /** | |
| * This will take . (dot) delimitted fields and allow you to assemble them into a complex heirarchical map. | |
| * | |
| * Primary purpose is eventual serialization into JSON or XML for instance. | |
| * | |
| * PathMapBuilder builder = new PathMapBuilder() | |
| * .add("a.b[0].d", "ab0d") | |
| * .add("a.b[1].d", "ab1d") | |
| * .add("a.b[1].e[0].g", "ab1e0g") | |
| * .add("a.b[1].e[0].f", "ab1e0f") | |
| * .add("a.x.z", "axz") | |
| * .add("a.x.y.z", 42) | |
| * | |
| * Would produce the following: | |
| * | |
| * [a:[b:[[d:ab0d], [d:ab1d, e:[[f:ab1e0f, g:ab1e0g]]]], x:[y:[z:42], z:axz]]] | |
| */ | |
| public class PathMapBuilder { | |
| private final SortedMap<String, Object> pathmap = Maps.newTreeMap(); | |
| private final Splitter pathSplitter; | |
| private static final Pattern ARRAY_EXTRACTOR = Pattern.compile("(.*)\\[(\\d+)\\]"); | |
| public PathMapBuilder(){ | |
| pathSplitter = Splitter.on('.'); | |
| } | |
| public synchronized PathMapBuilder add(String key, Object value){ | |
| pathmap.put(key,value); | |
| return this; | |
| } | |
| public synchronized ImmutableMap<String,Object> build() { | |
| final Map<String,Object> builder = Maps.newHashMap(); | |
| for (Map.Entry<String, Object> stringObjectEntry : pathmap.entrySet()) { | |
| Map<String,Object> valueHolder = builder; | |
| final List<String> splitPath = pathSplitter.splitToList(stringObjectEntry.getKey()); | |
| for (int i = 0; i < splitPath.size(); i++) { | |
| final String currentPath = splitPath.get(i); | |
| final Matcher arrayMatcher = ARRAY_EXTRACTOR.matcher(currentPath); | |
| if(arrayMatcher.matches()){ | |
| String arrayName = arrayMatcher.group(1); | |
| int arrayIndex = Integer.valueOf(arrayMatcher.group(2)); | |
| List<Object> array; | |
| if(valueHolder.containsKey(arrayName)){ | |
| array = (List<Object>)valueHolder.get(arrayName); | |
| }else{ | |
| array = Lists.newLinkedList(); | |
| valueHolder.put(arrayName, array); | |
| } | |
| if(i == splitPath.size()-1){ | |
| array.add(stringObjectEntry.getValue()); | |
| }else{ | |
| while(array.size() < arrayIndex+1){ | |
| array.add(Maps.newHashMap()); | |
| } | |
| valueHolder = (Map)array.get(arrayIndex); | |
| } | |
| }else if(i == splitPath.size()-1){ | |
| valueHolder.put(currentPath, stringObjectEntry.getValue()); | |
| }else{ | |
| if(!valueHolder.containsKey(currentPath)){ | |
| valueHolder.put(currentPath, Maps.newHashMap()); | |
| } | |
| Object pathObject = valueHolder.get(currentPath); | |
| if(pathObject instanceof Map) { | |
| valueHolder = (Map<String, Object>) pathObject; | |
| }else{ | |
| throw new UnsupportedOperationException("We expect non terminal entries to be objects."); | |
| } | |
| } | |
| } | |
| } | |
| return ImmutableMap.copyOf(builder); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package edu.mayo.cim.mxd.fileloader | |
| import com.fasterxml.jackson.databind.ObjectMapper | |
| import com.google.common.collect.ImmutableMap | |
| import spock.lang.Specification | |
| /** | |
| * Created by ejwinter on 2/16/17. | |
| */ | |
| class PathMapBuilderSpec extends Specification { | |
| def "we can use it to fill in a complext tree of values"(){ | |
| given: | |
| PathMapBuilder builder = new PathMapBuilder() | |
| .add("a.b.c","abc") | |
| .add("a.b.d", "abd") | |
| .add("a.x.z", "axz") | |
| .add("a.x.y.z", "axyz") | |
| when: | |
| ImmutableMap<String,Object> built = builder.build() | |
| then: | |
| Map<String,Object> a = built.get("a") | |
| Map<String,Object> x = a.get("x") | |
| Map<String,Object> y = x.get("y") | |
| y.get("z") == "axyz" | |
| } | |
| def "it can hold various data types"(){ | |
| given: | |
| PathMapBuilder builder = new PathMapBuilder() | |
| .add("a.b.c","abc") | |
| .add("a.b.d", "abd") | |
| .add("a.x.z", "axz") | |
| .add("a.x.y.z", 42) | |
| when: | |
| ImmutableMap<String,Object> built = builder.build() | |
| then: | |
| Map<String,Object> a = built.get("a") | |
| Map<String,Object> x = a.get("x") | |
| Map<String,Object> y = x.get("y") | |
| y.get("z") == 42 | |
| } | |
| def "it can be mapped to json"(){ | |
| given: | |
| PathMapBuilder builder = new PathMapBuilder() | |
| .add("a.b.c","abc") | |
| .add("a.x.y.z", 42) | |
| when: | |
| ImmutableMap<String,Object> built = builder.build() | |
| then: | |
| new ObjectMapper().writeValueAsString(built) == "{\"a\":{\"b\":{\"c\":\"abc\"},\"x\":{\"y\":{\"z\":42}}}}" | |
| } | |
| def "it can handle lists"(){ | |
| given: | |
| PathMapBuilder builder = new PathMapBuilder() | |
| .add("a.b[0].d", "ab0d") | |
| .add("a.b[1].d", "ab1d") | |
| .add("a.b[1].e[0].g", "ab1e0g") | |
| .add("a.b[1].e[0].f", "ab1e0f") | |
| .add("a.x.z", "axz") | |
| .add("a.x.y.z", 42) | |
| when: | |
| ImmutableMap<String,Object> built = builder.build() | |
| then: | |
| Map<String,Object> a = built.get("a") | |
| Map<String,Object> b0 = a.get("b")[0] | |
| b0.get("d") == "ab0d" | |
| Map<String,Object> b1 = a.get("b")[1] | |
| b1.get("d") == "ab1d" | |
| and: | |
| new ObjectMapper().writeValueAsString(built) == "{\"a\":{\"b\":[{\"d\":\"ab0d\"},{\"d\":\"ab1d\",\"e\":[{\"f\":\"ab1e0f\",\"g\":\"ab1e0g\"}]}],\"x\":{\"y\":{\"z\":42},\"z\":\"axz\"}}}" | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was a handy utility class used when we tried to turn a table with headers and row values into complex json objects.