package org.mvel2.ast; import org.mvel2.CompileException; import org.mvel2.ParserContext; import org.mvel2.UnresolveablePropertyException; import org.mvel2.compiler.ExecutableStatement; import org.mvel2.integration.VariableResolver; import org.mvel2.integration.VariableResolverFactory; import org.mvel2.integration.impl.MapVariableResolverFactory; import org.mvel2.integration.impl.SimpleValueResolver; import org.mvel2.util.CallableProxy; import org.mvel2.util.SimpleIndexHashMapWrapper; import java.util.Collection; import java.util.Map; import java.util.Set; import static org.mvel2.DataConversion.canConvert; import static org.mvel2.DataConversion.convert; public class Proto extends ASTNode { private String name; private Map<String, Receiver> receivers; private int cursorStart; private int cursorEnd; public Proto(String name, ParserContext pCtx) { super(pCtx); this.name = name; this.receivers = new SimpleIndexHashMapWrapper<String, Receiver>(); } public Receiver declareReceiver(String name, Function function) { Receiver r = new Receiver(null, ReceiverType.FUNCTION, function); receivers.put(name, r); return r; } public Receiver declareReceiver(String name, Class type, ExecutableStatement initCode) { Receiver r = new Receiver(null, ReceiverType.PROPERTY, initCode); receivers.put(name, r); return r; } public Receiver declareReceiver(String name, ReceiverType type, ExecutableStatement initCode) { Receiver r = new Receiver(null, type, initCode); receivers.put(name, r); return r; } public ProtoInstance newInstance(Object ctx, Object thisCtx, VariableResolverFactory factory) { return new ProtoInstance(this, ctx, thisCtx, factory); } @Override public Object getReducedValue(Object ctx, Object thisValue, VariableResolverFactory factory) { factory.createVariable(name, this); return this; } @Override public Object getReducedValueAccelerated(Object ctx, Object thisValue, VariableResolverFactory factory) { factory.createVariable(name, this); return this; } public class Receiver implements CallableProxy { private ReceiverType type; private Object receiver; private ExecutableStatement initValue; private ProtoInstance instance; public Receiver(ProtoInstance protoInstance, ReceiverType type, Object receiver) { this.instance = protoInstance; this.type = type; this.receiver = receiver; } public Receiver(ProtoInstance protoInstance, ReceiverType type, ExecutableStatement stmt) { this.instance = protoInstance; this.type = type; this.initValue = stmt; } public Object call(Object ctx, Object thisCtx, VariableResolverFactory factory, Object[] parms) { switch (type) { case FUNCTION: return ((Function) receiver).call(ctx, thisCtx, new InvokationContextFactory(factory, instance.instanceStates), parms); case PROPERTY: return receiver; case DEFERRED: throw new CompileException("unresolved prototype receiver", expr, start); } return null; } public Receiver init(ProtoInstance instance, Object ctx, Object thisCtx, VariableResolverFactory factory) { return new Receiver(instance, type, type == ReceiverType.PROPERTY && initValue != null ? initValue.getValue(ctx, thisCtx, factory) : receiver); } public void setType(ReceiverType type) { this.type = type; } public void setInitValue(ExecutableStatement initValue) { this.initValue = initValue; } } public enum ReceiverType { DEFERRED, FUNCTION, PROPERTY } public class ProtoInstance implements Map<String, Receiver> { private Proto protoType; private VariableResolverFactory instanceStates; private SimpleIndexHashMapWrapper<String, Receiver> receivers; public ProtoInstance(Proto protoType, Object ctx, Object thisCtx, VariableResolverFactory factory) { this.protoType = protoType; receivers = new SimpleIndexHashMapWrapper<String, Receiver>(); for (Map.Entry<String, Receiver> entry : protoType.receivers.entrySet()) { receivers.put(entry.getKey(), entry.getValue().init(this, ctx, thisCtx, factory)); } instanceStates = new ProtoContextFactory(receivers); } public Proto getProtoType() { return protoType; } public int size() { return receivers.size(); } public boolean isEmpty() { return receivers.isEmpty(); } public boolean containsKey(Object key) { return receivers.containsKey(key); } public boolean containsValue(Object value) { return receivers.containsValue(value); } public Receiver get(Object key) { return receivers.get(key); } public Receiver put(String key, Receiver value) { return receivers.put(key, value); } public Receiver remove(Object key) { return receivers.remove(key); } public void putAll(Map m) { } public void clear() { } public Set<String> keySet() { return receivers.keySet(); } public Collection<Receiver> values() { return receivers.values(); } public Set<Entry<String, Receiver>> entrySet() { return receivers.entrySet(); } } @Override public String getName() { return name; } @Override public String toString() { return "proto " + name; } public class ProtoContextFactory extends MapVariableResolverFactory { private final SimpleIndexHashMapWrapper<String, VariableResolver> variableResolvers; public ProtoContextFactory(SimpleIndexHashMapWrapper variables) { super(variables); variableResolvers = new SimpleIndexHashMapWrapper<String, VariableResolver>(variables, true); } @Override public VariableResolver createVariable(String name, Object value) { VariableResolver vr; try { (vr = getVariableResolver(name)).setValue(value); return vr; } catch (UnresolveablePropertyException e) { addResolver(name, vr = new ProtoResolver(variables, name)).setValue(value); return vr; } } @Override public VariableResolver createVariable(String name, Object value, Class<?> type) { VariableResolver vr; try { vr = getVariableResolver(name); } catch (UnresolveablePropertyException e) { vr = null; } if (vr != null && vr.getType() != null) { throw new CompileException("variable already defined within scope: " + vr.getType() + " " + name, expr, start); } else { addResolver(name, vr = new ProtoResolver(variables, name, type)).setValue(value); return vr; } } @Override public void setIndexedVariableNames(String[] indexedVariableNames) { // } @Override public String[] getIndexedVariableNames() { // return null; } @Override public VariableResolver createIndexedVariable(int index, String name, Object value, Class<?> type) { VariableResolver vr = this.variableResolvers != null ? this.variableResolvers.getByIndex(index) : null; if (vr != null && vr.getType() != null) { throw new CompileException("variable already defined within scope: " + vr.getType() + " " + name, expr, start); } else { return createIndexedVariable(variableIndexOf(name), name, value); } } @Override public VariableResolver createIndexedVariable(int index, String name, Object value) { VariableResolver vr = variableResolvers.getByIndex(index); if (vr == null) { vr = new SimpleValueResolver(value); variableResolvers.putAtIndex(index, vr); } else { vr.setValue(value); } return indexedVariableResolvers[index]; } @Override public VariableResolver getIndexedVariableResolver(int index) { return variableResolvers.getByIndex(index); } @Override public VariableResolver setIndexedVariableResolver(int index, VariableResolver resolver) { variableResolvers.putAtIndex(index, resolver); return resolver; } @Override public int variableIndexOf(String name) { return variableResolvers.indexOf(name); } public VariableResolver getVariableResolver(String name) { VariableResolver vr = variableResolvers.get(name); if (vr != null) { return vr; } else if (variables.containsKey(name)) { variableResolvers.put(name, vr = new ProtoResolver(variables, name)); return vr; } else if (nextFactory != null) { return nextFactory.getVariableResolver(name); } throw new UnresolveablePropertyException("unable to resolve variable '" + name + "'"); } } public class ProtoResolver implements VariableResolver { private String name; private Class<?> knownType; private Map<String, Object> variableMap; public ProtoResolver(Map<String, Object> variableMap, String name) { this.variableMap = variableMap; this.name = name; } public ProtoResolver(Map<String, Object> variableMap, String name, Class knownType) { this.name = name; this.knownType = knownType; this.variableMap = variableMap; } public void setName(String name) { this.name = name; } public void setStaticType(Class knownType) { this.knownType = knownType; } public String getName() { return name; } public Class getType() { return knownType; } public void setValue(Object value) { if (knownType != null && value != null && value.getClass() != knownType) { if (!canConvert(knownType, value.getClass())) { throw new CompileException("cannot assign " + value.getClass().getName() + " to type: " + knownType.getName(), expr, start); } try { value = convert(value, knownType); } catch (Exception e) { throw new CompileException("cannot convert value of " + value.getClass().getName() + " to: " + knownType.getName(), expr, start); } } ((Receiver) variableMap.get(name)).receiver = value; } public Object getValue() { return ((Receiver) variableMap.get(name)).receiver; } public int getFlags() { return 0; } } public void setCursorPosition(int start, int end) { this.cursorStart = start; this.cursorEnd = end; } public int getCursorStart() { return cursorStart; } public int getCursorEnd() { return cursorEnd; } }