package io.cattle.platform.object.serialization.impl; import io.cattle.platform.json.JsonMapper; import io.cattle.platform.object.ObjectManager; import io.cattle.platform.object.meta.ObjectMetaDataManager; import io.cattle.platform.object.serialization.ObjectSerializer; import io.cattle.platform.object.serialization.ObjectSerializerFactory; import io.cattle.platform.object.serialization.ObjectTypeSerializerPostProcessor; import io.cattle.platform.util.type.CollectionUtils; import io.cattle.platform.util.type.InitializationTask; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import javax.inject.Inject; public class DefaultObjectSerializerFactoryImpl implements ObjectSerializerFactory, InitializationTask { private static final Pattern GOOD_CHARS = Pattern.compile("[a-z0-9]", Pattern.CASE_INSENSITIVE); JsonMapper jsonMapper; ObjectManager objectManager; ObjectMetaDataManager metaDataManager; Map<String, List<ObjectTypeSerializerPostProcessor>> postProcessorsMap = new HashMap<String, List<ObjectTypeSerializerPostProcessor>>(); List<ObjectTypeSerializerPostProcessor> postProcessors; @Override public ObjectSerializer compile(String type, String expression) { Action action = parseAction(type, expression); DefaultObjectSerializerImpl result = new DefaultObjectSerializerImpl(jsonMapper, objectManager, metaDataManager, postProcessorsMap, action, expression); result.serialize(null); return result; } @Override public void start() { for (ObjectTypeSerializerPostProcessor postProcessor : postProcessors) { for (String type : postProcessor.getTypes()) { CollectionUtils.addToMap(postProcessorsMap, type, postProcessor, ArrayList.class); } } } protected Action parseAction(String type, String expression) { /* Should use antlr if this gets too complicated */ Context c = new Context(); for (int i = 0; i < expression.length(); i++) { char current = expression.charAt(i); switch (current) { case '|': case ',': c = endWord(c, current); c.done = false; break; case '.': c.dotStart = true; c = c.push(); break; case '[': c = c.push(); break; case ']': c = endWord(c, current); c = c.pop(expression); break; default: if (!GOOD_CHARS.matcher(Character.toString(current)).matches() || c.done) { throw new IllegalArgumentException("Bad character [" + current + "] found in [" + expression + "]"); } c.currentWord.append(current); break; } } if (c.currentWord.length() > 0) { c = endWord(c, ' '); } if (c.parent != null) { throw new IllegalStateException("Missing closing ']' in [" + expression + "]"); } return new Action(type, c.currentActions); } protected static Context endWord(Context c, char current) { if (c.currentWord.length() == 0 && c.currentAction.getName() == null) { throw new IllegalStateException("got [" + current + "] but there is no word before it"); } c.currentAction.setName(c.currentWord.toString()); c.currentActions.add(c.currentAction); c.currentAction = new Action(); c.currentWord.setLength(0); if (current != '.') { if (c.parent != null && c.parent.dotStart) { return endWord(c.parent, current); } } return c; } private static final class Context { Context parent = null; List<Action> currentActions = new ArrayList<Action>(); Action currentAction = new Action(); StringBuilder currentWord = new StringBuilder(); boolean dotStart = false; boolean done = false; Context push() { Context child = new Context(); child.parent = this; currentAction.setChildren(child.currentActions); this.done = true; return child; } Context pop(String expression) { if (parent == null) { throw new IllegalStateException("To many closing braces in [" + expression + "]"); } return parent; } @Override public String toString() { Context current = this; while (current.parent != null) { current = current.parent; } if (current.currentWord.length() > 0) { List<Action> actions = new ArrayList<Action>(current.currentActions); actions.add(new Action(current.currentWord.toString() + "*", current.currentAction.getChildren())); return actions.toString(); } else { return current.currentActions.toString(); } } } public JsonMapper getJsonMapper() { return jsonMapper; } @Inject public void setJsonMapper(JsonMapper jsonMapper) { this.jsonMapper = jsonMapper; } public ObjectManager getObjectManager() { return objectManager; } @Inject public void setObjectManager(ObjectManager objectManager) { this.objectManager = objectManager; } public ObjectMetaDataManager getMetaDataManager() { return metaDataManager; } @Inject public void setMetaDataManager(ObjectMetaDataManager metaDataManager) { this.metaDataManager = metaDataManager; } public List<ObjectTypeSerializerPostProcessor> getPostProcessors() { return postProcessors; } @Inject public void setPostProcessors(List<ObjectTypeSerializerPostProcessor> postProcessors) { this.postProcessors = postProcessors; } }