blob: b62ef9737016c74316370e5d8637d0633da66799 [file] [log] [blame]
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 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 HashMap<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 HashMap<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) {
}
}