diff --git a/yagi/src/main/java/org/raml/yagi/framework/model/ModelProxyBuilder.java b/yagi/src/main/java/org/raml/yagi/framework/model/ModelProxyBuilder.java index 091938ff..7620db08 100644 --- a/yagi/src/main/java/org/raml/yagi/framework/model/ModelProxyBuilder.java +++ b/yagi/src/main/java/org/raml/yagi/framework/model/ModelProxyBuilder.java @@ -22,7 +22,10 @@ import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; import javax.annotation.Nullable; @@ -43,10 +46,48 @@ public static T createModel(Class apiInterface, NodeModel delegateNode, M new SimpleProxy(delegateNode, bindingConfiguration)); } + private static class MethodCacheKey + { + Method method; + Class delegateClass; + + public MethodCacheKey(Method method, Class delegateClass) + { + this.method = method; + this.delegateClass = delegateClass; + } + + @Override + public int hashCode() + { + return Objects.hash(delegateClass, method); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof MethodCacheKey)) + { + return false; + } + MethodCacheKey other = (MethodCacheKey) obj; + return Objects.equals(delegateClass, other.delegateClass) && Objects.equals(method, other.method); + } + } + private static class SimpleProxy implements InvocationHandler { private NodeModel delegate; private ModelBindingConfiguration bindingConfiguration; + private static Map methodCache = new HashMap<>(); public SimpleProxy(NodeModel delegate, ModelBindingConfiguration bindingConfiguration) { @@ -207,12 +248,26 @@ else if (returnClass.equals(Object.class)) @Nullable private Method findMatchingMethod(Method method) { + MethodCacheKey cacheKey = new MethodCacheKey(method, delegate.getClass()); + Object cachedMethod = methodCache.get(cacheKey); + if (cachedMethod instanceof Method) + { + return (Method) cachedMethod; + } + else if (cachedMethod instanceof String) + { + return null; + } + try { - return delegate.getClass().getMethod(method.getName(), method.getParameterTypes()); + cachedMethod = delegate.getClass().getMethod(method.getName(), method.getParameterTypes()); + methodCache.put(cacheKey, cachedMethod); + return (Method) cachedMethod; } catch (NoSuchMethodException e) { + methodCache.put(cacheKey, "no such method"); return null; } } diff --git a/yagi/src/main/java/org/raml/yagi/framework/model/PackageModelBinding.java b/yagi/src/main/java/org/raml/yagi/framework/model/PackageModelBinding.java index f7a77169..c6cd971d 100644 --- a/yagi/src/main/java/org/raml/yagi/framework/model/PackageModelBinding.java +++ b/yagi/src/main/java/org/raml/yagi/framework/model/PackageModelBinding.java @@ -15,11 +15,16 @@ */ package org.raml.yagi.framework.model; +import java.util.HashMap; +import java.util.Map; + import javax.annotation.Nullable; public class PackageModelBinding implements ModelBinding { + private static final ClassNodeModelFactory NULL_WRAPPER = new ClassNodeModelFactory(null); private String packageName; + private Map, ClassNodeModelFactory> cache = new HashMap<>(); public PackageModelBinding(String packageName) { @@ -30,15 +35,24 @@ public PackageModelBinding(String packageName) @Override public NodeModelFactory binding(Class clazz) { - final String simpleName = clazz.getSimpleName(); + ClassNodeModelFactory factory = cache.get(clazz); + if (factory != null) + { + return (factory == NULL_WRAPPER) ? null : factory; + } + try { + final String simpleName = clazz.getSimpleName(); final Class aClass = Class.forName(packageName + "." + simpleName); - return new ClassNodeModelFactory((Class) aClass); + factory = new ClassNodeModelFactory((Class) aClass); + cache.put(clazz, factory); + return factory; } catch (ClassNotFoundException e) { // No binding available + cache.put(clazz, NULL_WRAPPER); return null; } } diff --git a/yagi/src/main/java/org/raml/yagi/framework/nodes/BaseNode.java b/yagi/src/main/java/org/raml/yagi/framework/nodes/BaseNode.java index 35d8e359..b4d3f1b6 100644 --- a/yagi/src/main/java/org/raml/yagi/framework/nodes/BaseNode.java +++ b/yagi/src/main/java/org/raml/yagi/framework/nodes/BaseNode.java @@ -15,14 +15,17 @@ */ package org.raml.yagi.framework.nodes; -import org.raml.yagi.framework.util.NodeSelector; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.raml.yagi.framework.util.NodeSelector; public abstract class BaseNode implements Node { @@ -34,6 +37,8 @@ public abstract class BaseNode implements Node private List annotations = new ArrayList<>(); + private Map, List> descendantsCache = new HashMap<>(); + public BaseNode() { } @@ -79,12 +84,14 @@ public void addChild(Node node) { node.setParent(this); children.add(node); + clearCache(); } @Override public void removeChild(Node node) { children.remove(node); + clearCache(); } @Override @@ -102,21 +109,46 @@ public Node getContextNode() return contextNode; } + @SuppressWarnings("unchecked") @Nonnull @Override public List findDescendantsWith(Class nodeType) { + if (children.isEmpty()) + { + return Collections.emptyList(); + } + + List descendants = descendantsCache.get(nodeType); + if (descendants != null) + { + return (List) descendants; + } final List result = new ArrayList<>(); - final List children = getChildren(); - for (Node child : children) + collectDescendantsWithType(this, nodeType, result); + descendantsCache.put(nodeType, result); + return result; + } + + private void clearCache() + { + descendantsCache.clear(); + if (parent != null) + { + ((BaseNode) parent).clearCache(); + } + } + + private static void collectDescendantsWithType(Node node, Class nodeType, List descendants) + { + for (Node child : node.getChildren()) { if (nodeType.isAssignableFrom(child.getClass())) { - result.add(nodeType.cast(child)); + descendants.add(nodeType.cast(child)); } - result.addAll(child.findDescendantsWith(nodeType)); + collectDescendantsWithType(child, nodeType, descendants); } - return result; } @Nullable @@ -145,6 +177,7 @@ public void replaceWith(Node newNode) { newNode.addChild(child); } + clearCache(); } } @@ -165,6 +198,7 @@ public void replaceTree(Node newSubTree) } getParent().setChild(idx, newSubTree); } + clearCache(); } } @@ -176,6 +210,7 @@ public void removeChildren() child.setParent(null); } children.clear(); + clearCache(); } @Override @@ -183,6 +218,7 @@ public void setChild(int idx, Node newNode) { children.set(idx, newNode); newNode.setParent(this); + clearCache(); } @Override @@ -190,6 +226,7 @@ public void addChild(int idx, Node newNode) { children.add(idx, newNode); newNode.setParent(this); + clearCache(); } @Override @@ -200,12 +237,14 @@ public void setParent(Node parent) this.parent.removeChild(this); } this.parent = parent; + clearCache(); } @Override public void setSource(Node source) { this.source = source; + clearCache(); } @Override