| /******************************************************************************* |
| * Copyright (c) 2010 xored software, Inc. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * xored software, Inc. - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.javascript.ti; |
| |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.dltk.internal.javascript.validation.JavaScriptValidations; |
| import org.eclipse.dltk.javascript.typeinference.IValueReference; |
| 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; |
| |
| public abstract class AbstractReference implements IValueReference, |
| IValueProvider { |
| |
| public abstract IValue getValue(); |
| |
| public abstract IValue createValue(); |
| |
| public void setValue(IValueReference value) { |
| IValue val = createValue(); |
| if (val != null && value != null) { |
| IValue src = ((IValueProvider) value).getValue(); |
| if (src == PhantomValue.VALUE) { |
| if (val.getDeclaredTypes().isEmpty()) { |
| val.addReference(src); |
| } |
| return; |
| } |
| if (src == null) { |
| src = new LazyReferenceValue(value); |
| val.addReference(src); |
| return; |
| } |
| val.clear(); |
| // if value is a set to be a reference but the src is of type |
| // function then still add it (a function reference should really |
| // overwrite the current value |
| if (src instanceof Value |
| && (((IValueProvider) value).isReference() && !(src |
| .getDeclaredType() != null && src.getKind() == ReferenceKind.FUNCTION)) |
| || value.isParentOf(this)) { |
| val.addReference(src); |
| } else { |
| val.addValue(src); |
| } |
| } |
| } |
| |
| public void addValue(IValueReference value, boolean copy) { |
| IValue val = createValue(); |
| if (val != null && value != null) { |
| IValue src = ((IValueProvider) value).getValue(); |
| if (src == PhantomValue.VALUE) { |
| if (val.getDeclaredTypes().isEmpty()) { |
| val.addReference(src); |
| } |
| return; |
| } |
| if (src == null) { |
| src = new LazyReferenceValue(value); |
| val.addReference(src); |
| return; |
| } |
| if (!copy || src instanceof Value |
| && ((IValueProvider) value).isReference() |
| || value.isParentOf(this)) { |
| val.addReference(src); |
| } else { |
| val.addValue(src); |
| } |
| } |
| } |
| |
| public void removeReference(IValueReference reference) { |
| IValue value = getValue(); |
| if (value != null && reference != null |
| && reference instanceof IValueProvider) { |
| value.removeReference(((IValueProvider) reference).getValue()); |
| } |
| } |
| |
| public boolean isParentOf(IValueReference anotherReference) { |
| while (anotherReference != null) { |
| if (this.equals(anotherReference)) { |
| return true; |
| } |
| anotherReference = anotherReference.getParent(); |
| } |
| return false; |
| } |
| |
| public void clear() { |
| IValue value = getValue(); |
| if (value != null) { |
| value.clear(); |
| } |
| } |
| |
| public boolean exists() { |
| return getValue() != null; |
| } |
| |
| public final Object getAttribute(String key) { |
| return getAttribute(key, false); |
| } |
| |
| public Object getAttribute(String key, boolean includeReferences) { |
| IValue value = getValue(); |
| return value != null ? value.getAttribute(key, includeReferences) |
| : null; |
| } |
| |
| public IRType getDeclaredType() { |
| IValue value = getValue(); |
| return value != null ? value.getDeclaredType() : null; |
| } |
| |
| public JSTypeSet getDeclaredTypes() { |
| IValue value = getValue(); |
| return value != null ? value.getDeclaredTypes() : JSTypeSet.emptySet(); |
| } |
| |
| public ReferenceKind getKind() { |
| IValue value = getValue(); |
| return value != null ? value.getKind() : ReferenceKind.UNKNOWN; |
| } |
| |
| public ReferenceLocation getLocation() { |
| IValue value = getValue(); |
| return value != null ? value.getLocation() : ReferenceLocation.UNKNOWN; |
| } |
| |
| public JSTypeSet getTypes() { |
| IValue value = getValue(); |
| return value != null ? value.getTypes() : JSTypeSet.emptySet(); |
| } |
| |
| public void setAttribute(String key, Object value) { |
| IValue val = createValue(); |
| if (val != null) { |
| val.setAttribute(key, value); |
| } |
| } |
| |
| public void setDeclaredType(IRType type) { |
| IValue value = createValue(); |
| if (value != null) { |
| value.setDeclaredType(type); |
| } |
| } |
| |
| public void setKind(ReferenceKind kind) { |
| IValue value = createValue(); |
| if (value != null) { |
| value.setKind(kind); |
| } |
| } |
| |
| public void setLocation(ReferenceLocation location) { |
| IValue value = createValue(); |
| if (value != null) { |
| value.setLocation(location); |
| } |
| } |
| |
| public IValueReference getChild(String name) { |
| return new ChildReference(this, name); |
| } |
| |
| public IValueReference createChild(String name) { |
| createValue().createChild(name, IValue.CREATE); |
| return getChild(name); |
| } |
| |
| public boolean hasChild(String name) { |
| IValue value = getValue(); |
| return value != null && value.getChild(name, true) != null; |
| } |
| |
| public final Set<String> getDirectChildren() { |
| return getDirectChildren(IValue.DEFAULT); |
| } |
| |
| public Set<String> getDirectChildren(int flags) { |
| final IValue value = getValue(); |
| return value != null ? value.getDirectChildren(flags) : Collections |
| .<String> emptySet(); |
| } |
| |
| public Set<String> getDeletedChildren() { |
| final IValue value = getValue(); |
| return value != null ? value.getDeletedChildren() : Collections |
| .<String> emptySet(); |
| } |
| |
| private final static class LazyReferenceValue extends Value implements |
| ILazyValue { |
| private final IValueReference reference; |
| private boolean resolved = false; |
| private boolean finalResolve; |
| private boolean typeResolved; |
| |
| public LazyReferenceValue(IValueReference value) { |
| this.reference = value; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof LazyReferenceValue) { |
| return reference.equals(((LazyReferenceValue) obj).reference); |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| return reference.hashCode(); |
| } |
| |
| public void resolve() { |
| if (!resolved) { |
| resolved = true; |
| IValue value = ((IValueProvider) reference).getValue(); |
| if (value != null) { |
| if (finalResolve && value instanceof Value) { |
| ((Value) value).resolveLazyValues(new HashSet<Value>()); |
| } |
| if (value instanceof Value |
| && ((IValueProvider) reference).isReference()) { |
| addReference(value); |
| } else { |
| addValue(value); |
| } |
| } else if (!reference.getName().equals(FUNCTION_OP)) { |
| boolean resolvedToType = false; |
| // if not already in the final resolve, try to look if it |
| // did already resolve up the chain |
| // to a value that already resolved to a type, if that is |
| // the case then this lazy reference is |
| // most likely just never going to hit on something known. |
| if (!finalResolve && !typeResolved) { |
| typeResolved = true; |
| IValueReference parent = reference.getParent(); |
| while (parent != null) { |
| IRType type = JavaScriptValidations.typeOf(parent); |
| if (type != null) { |
| resolvedToType = !(type instanceof IRLocalType); |
| break; |
| } |
| parent = parent.getParent(); |
| } |
| } |
| resolved = finalResolve || resolvedToType; |
| } else { |
| resolved = false; |
| } |
| } |
| } |
| |
| public void setFinalResolve() { |
| finalResolve = true; |
| } |
| |
| public String getLazyName() { |
| return null; |
| } |
| |
| public boolean isResolved() { |
| return resolved; |
| } |
| |
| @Override |
| public String toString() { |
| return "LazyReferenceValue[resolved:" + resolved + ",reference:" |
| + reference + ']'; |
| } |
| |
| } |
| } |