package org.eclipse.dltk.internal.javascript.ti; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.dltk.javascript.typeinference.ReferenceKind; import org.eclipse.dltk.javascript.typeinference.ReferenceLocation; import org.eclipse.dltk.javascript.typeinfo.IRLocalType; import org.eclipse.dltk.javascript.typeinfo.IRType; import org.eclipse.dltk.javascript.typeinfo.JSTypeSet; import org.eclipse.dltk.javascript.typeinfo.RTypes; public class ImmutableValue implements IValue, IValue2 { private Map<String, IValue> elementValues; protected IRType declaredType; protected final JSTypeSet types; protected Set<String> deletedChildren; protected ReferenceKind kind = ReferenceKind.UNKNOWN; protected ReferenceLocation location = ReferenceLocation.UNKNOWN; protected final Map<String, ImmutableValue> children; protected final Map<String, IValue> inherited; protected final Set<IValue> references; protected Map<String, Object> attributes; protected static interface Handler<R> { void process(ImmutableValue value, R result); } protected static interface Handler2<R> extends Handler<R> { void processOther(IValue value, R result); } protected ImmutableValue() { super(); types = JSTypeSet.create(); children = new HashMap<String, ImmutableValue>(4, 0.9f); inherited = new HashMap<String, IValue>(4, 0.9f); references = new HashSet<IValue>(4, 0.9f); } public ImmutableValue(IRType declaredType, JSTypeSet types, Set<String> deletedChildren, ReferenceKind kind, ReferenceLocation location, Map<String, ImmutableValue> children, Map<String, IValue> inherited, Set<IValue> references, Map<String, Object> attributes) { this.declaredType = declaredType; this.types = types; this.deletedChildren = deletedChildren; this.kind = kind; this.location = location; this.children = children; this.inherited = inherited; this.references = references; this.attributes = attributes; } protected ImmutableValue(ImmutableValue value) { this.declaredType = value.declaredType; this.types = value.types; this.deletedChildren = value.deletedChildren; this.kind = value.kind; this.location = value.location; this.children = value.children; this.inherited = value.inherited; this.references = value.references; this.attributes = value.attributes; } protected final boolean hasReferences() { return !references.isEmpty(); } public Set<? extends IValue> getReferences() { return references; } protected static <R> void execute(ImmutableValue value, Handler<R> handler, R result, Set<IValue> visited) { if (visited.add(value)) { if (value instanceof ILazyValue) ((ILazyValue) value).resolve(); handler.process(value, result); for (IValue child : value.references) { if (child instanceof ImmutableValue) execute((ImmutableValue) child, handler, result, visited); else if (handler instanceof Handler2) { ((Handler2<R>) handler).processOther(child, result); } } } } private static final Handler<JSTypeSet> GET_TYPES = new Handler2<JSTypeSet>() { public void process(ImmutableValue value, JSTypeSet result) { result.addAll(value.types); } public void processOther(IValue value, JSTypeSet result) { result.addAll(value.getTypes()); }; }; private static final Handler<JSTypeSet> GET_DECLARED_TYPES = new Handler<JSTypeSet>() { public void process(ImmutableValue value, JSTypeSet result) { if (value.declaredType != null) result.add(value.declaredType); } }; public JSTypeSet getTypes() { if (hasReferences()) { final JSTypeSet result = JSTypeSet.create(); execute(this, GET_TYPES, result, new HashSet<IValue>()); return result; } else { return types; } } private static final Handler<Set<String>> GET_DIRECT_CHILDREN = new Handler<Set<String>>() { public void process(ImmutableValue value, Set<String> result) { result.addAll(value.children.keySet()); } }; public IRType getDeclaredType() { if (declaredType != null) { return declaredType; } else if (hasReferences()) { final JSTypeSet result = JSTypeSet.create(); execute(this, GET_DECLARED_TYPES, result, new HashSet<IValue>()); return result.toRType(); } else { return null; } } public JSTypeSet getDeclaredTypes() { if (declaredType != null) { return JSTypeSet.singleton(declaredType); } else if (hasReferences()) { final JSTypeSet result = JSTypeSet.create(); execute(this, GET_DECLARED_TYPES, result, new HashSet<IValue>()); return result; } else { return JSTypeSet.emptySet(); } } public ReferenceKind getKind() { return kind; } public ReferenceLocation getLocation() { return location; } public final Object getAttribute(String key) { return getAttribute(key, false); } public Object getAttribute(String key, boolean includeReferences) { if (IReferenceAttributes.PHANTOM.equals(key) && declaredType == RTypes.any()) { return Boolean.TRUE; } Object attribute = null; if (attributes != null) { attribute = attributes.get(key); } if (includeReferences && attribute == null && !references.isEmpty()) { attribute = visitReferenceForAttribute(key, new HashSet<ImmutableValue>()); } return attribute; } /** * @param key * @param attribute * @param visited * @return */ private Object visitReferenceForAttribute(String key, Set<ImmutableValue> visited) { if (visited.add(this)) { for (IValue reference : references) { Object attribute = reference.getAttribute(key, false); if (attribute != null) return attribute; if (reference instanceof ImmutableValue) { attribute = ((ImmutableValue) reference) .visitReferenceForAttribute(key, visited); if (attribute != null) return attribute; } } } return null; } protected static class GetChildHandler implements Handler2<Set<IValue>> { private final String childName; public GetChildHandler(String childName) { this.childName = childName; } public void process(ImmutableValue value, Set<IValue> result) { ImmutableValue child = value.children.get(childName); if (child != null) { result.add(child); } else { IValue member = ElementValue.findMemberA(value.declaredType, childName, true); if (member != null) { result.add(member); } final JSTypeSet valueTypes; if (value.hasReferences()) { valueTypes = value.types; } else { valueTypes = value.getTypes(); } for (IRType type : valueTypes) { member = ElementValue.findMemberA(type, childName, true); if (member != null) { result.add(member); } } } } public void processOther(IValue value, Set<IValue> result) { if (value == PhantomValue.VALUE) { result.add(value); } else { IValue childValue = value.getChild(childName, true); if (childValue != null) result.add(childValue); } } } public Set<String> getDirectChildren(int flags) { final Set<String> result = new HashSet<String>(); if (hasReferences()) { execute(this, GET_DIRECT_CHILDREN, result, new HashSet<IValue>()); } else { result.addAll(children.keySet()); } if ((flags & NO_LOCAL_TYPES) == 0) { if (getDeclaredType() instanceof IRLocalType) { result.addAll(((IRLocalType) getDeclaredType()) .getDirectChildren()); } for (IRType irType : getTypes()) { if (irType instanceof IRLocalType) { result.addAll(((IRLocalType) irType).getDirectChildren()); } } } return result; } public Set<String> getDeletedChildren() { if (deletedChildren != null) { return deletedChildren; } else { return Collections.emptySet(); } } protected IValue findMember(String name, boolean resolve) { if (declaredType == RTypes.any()) { return PhantomValue.VALUE; } IValue value = null; if (elementValues != null) value = elementValues.get(name); if (value == null && (declaredType != null || !types.isEmpty())) { if (declaredType != null) { value = ElementValue.findMemberA(declaredType, name, resolve); if (value != null) { if (elementValues == null) elementValues = new ConcurrentHashMap<String, IValue>( 4, 0.9f); elementValues.put(name, value); return value; } } for (IRType type : types) { value = ElementValue.findMemberA(type, name, resolve); if (value != null) { if (elementValues == null) elementValues = new ConcurrentHashMap<String, IValue>( 4, 0.9f); if (resolve && value instanceof ElementValue) { value = ((ElementValue) value).resolveValue(); } elementValues.put(name, value); return value; } } } return value; } public IValue getChild(String name, boolean resolve) { // first always try the value itself. // if found that this will always be the child to return IValue child = children.get(name); if (child == null) { child = inherited.get(name); if (child == null) { child = findMember(name, resolve); } } // if it didn't find a child in it self and it has references. // search of them. if (child == null && hasReferences()) { Set<IValue> result = new HashSet<IValue>(); execute(this, new GetChildHandler(name), result, new HashSet<IValue>()); if (!result.isEmpty()) { if (result.size() > 1) { // try to return the best match? (or should we combine // them??) Iterator<IValue> iterator = result.iterator(); IValue first = iterator.next(); while (iterator.hasNext()) { IValue next = iterator.next(); if (next.getDeclaredTypes().size() > first .getDeclaredTypes().size()) { first = next; continue; } if (next.getTypes().size() > first.getTypes().size() && first.getDeclaredTypes().size() == 0) { first = next; } } return first; } return result.iterator().next(); } else { return findMember(name, resolve); } } return child; } public boolean hasChild(String name) { return children.containsKey(name) || inherited.containsKey(name); } public IValue createChild(String name, int flags) { return null; } protected void childCreated(String name) { if (elementValues != null) { elementValues.remove(name); } if (deletedChildren != null) { deletedChildren.remove(name); } } public void setDeclaredType(IRType declaredType) { } public void addType(IRType type) { } public void setAttribute(String key, Object value) { } public void setKind(ReferenceKind kind) { } public void setLocation(ReferenceLocation location) { } public void addValue(IValue src) { } public void addReference(IValue src) { } public void removeReference(IValue value) { } public void clear() { } public void putChild(String name, IValue value) { } public void deleteChild(String name, boolean force) { } }