package org.rascalmpl.value.impl.reference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.rascalmpl.value.IAnnotatable;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IWithKeywordParameters;
import org.rascalmpl.value.exceptions.FactTypeUseException;
import org.rascalmpl.value.exceptions.UnexpectedChildTypeException;
import org.rascalmpl.value.impl.AbstractDefaultAnnotatable;
import org.rascalmpl.value.impl.AbstractDefaultWithKeywordParameters;
import org.rascalmpl.value.impl.AnnotatedConstructorFacade;
import org.rascalmpl.value.impl.ConstructorWithKeywordParametersFacade;
import org.rascalmpl.value.impl.func.NodeFunctions;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.value.visitors.IValueVisitor;
import io.usethesource.capsule.AbstractSpecialisedImmutableMap;
import io.usethesource.capsule.ImmutableMap;
/**
* Implementation of a typed tree node with access to children via labels
*/
public class Constructor extends Node implements IConstructor {
/*package*/ Constructor(Type type, IValue[] children) {
super(type.getName(), type, children);
}
/*package*/ Constructor(Type type) {
this(type, new IValue[0]);
}
/*package*/ Constructor(Type type, IValue[] children, Map<String,IValue> kwParams) {
this(type, getAllChildren(type, children, kwParams));
}
private static IValue[] getAllChildren(Type type, IValue[] children, Map<String, IValue> kwParams) {
IValue[] allChildren = new IValue[children.length + kwParams.size()];
System.arraycopy(children, 0, allChildren, 0, children.length);
for (Entry<String,IValue> entry : kwParams.entrySet()) {
allChildren[type.getFieldIndex(entry.getKey())] = entry.getValue();
}
return allChildren;
}
private Constructor(Constructor other, int childIndex, IValue newChild) {
super(other, childIndex, newChild);
}
@Override
public Type getType() {
return getConstructorType().getAbstractDataType();
}
@Override
public Type getUninstantiatedConstructorType() {
return fType;
}
public Type getConstructorType() {
if (fType.getAbstractDataType().isParameterized()) {
assert fType.getAbstractDataType().isOpen();
// this assures we always have the most concrete type for constructors.
Type[] actualTypes = new Type[fChildren.length];
for (int i = 0; i < fChildren.length; i++) {
actualTypes[i] = fChildren[i].getType();
}
Map<Type,Type> bindings = new HashMap<Type,Type>();
fType.getFieldTypes().match(TypeFactory.getInstance().tupleType(actualTypes), bindings);
for (Type field : fType.getAbstractDataType().getTypeParameters()) {
if (!bindings.containsKey(field)) {
bindings.put(field, TypeFactory.getInstance().voidType());
}
}
return fType.instantiate(bindings);
}
return fType;
}
public IValue get(String label) {
return super.get(fType.getFieldIndex(label));
}
public Type getChildrenTypes() {
return fType.getFieldTypes();
}
@Override
public IConstructor set(int i, IValue newChild) throws IndexOutOfBoundsException {
checkChildType(i, newChild);
return new Constructor(this, i, newChild);
}
public IConstructor set(String label, IValue newChild) throws FactTypeUseException {
int childIndex = fType.getFieldIndex(label);
checkChildType(childIndex, newChild);
return new Constructor(this, childIndex, newChild);
}
private void checkChildType(int i, IValue newChild) {
Type type = newChild.getType();
Type expectedType = getConstructorType().getFieldType(i);
if (!type.isSubtypeOf(expectedType)) {
throw new UnexpectedChildTypeException(expectedType, type);
}
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
else if(obj == null) {
return false;
}
else if (getClass() == obj.getClass()) {
Constructor other = (Constructor) obj;
return fType.comparable(other.fType) && super.equals(obj);
}
return false;
}
@Override
public boolean isEqual(IValue value) {
return NodeFunctions.isEqual(getValueFactory(), this, value);
}
@Override
public int hashCode() {
return 17 + ~super.hashCode();
}
@Override
public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E {
return v.visitConstructor(this);
}
public boolean declaresAnnotation(TypeStore store, String label) {
return store.getAnnotationType(getType(), label) != null;
}
public boolean has(String label) {
return getConstructorType().hasField(label);
}
/**
* TODO: Create and move to {@link AbstractConstructor}.
*/
@Override
public boolean isAnnotatable() {
return true;
}
/**
* TODO: Create and move to {@link AbstractConstructor}.
*/
@Override
public IAnnotatable<? extends IConstructor> asAnnotatable() {
return new AbstractDefaultAnnotatable<IConstructor>(this) {
@Override
protected IConstructor wrap(IConstructor content,
ImmutableMap<String, IValue> annotations) {
return new AnnotatedConstructorFacade(content, annotations);
}
};
}
@Override
public boolean mayHaveKeywordParameters() {
return true;
}
@Override
public IWithKeywordParameters<IConstructor> asWithKeywordParameters() {
return new AbstractDefaultWithKeywordParameters<IConstructor>(this, AbstractSpecialisedImmutableMap.<String,IValue>mapOf()) {
@Override
protected IConstructor wrap(IConstructor content, ImmutableMap<String, IValue> parameters) {
return new ConstructorWithKeywordParametersFacade(content, parameters);
}
@Override
public boolean hasParameters() {
return parameters != null && parameters.size() > 0;
}
@Override
public java.util.Set<String> getParameterNames() {
return parameters.keySet();
}
@Override
public Map<String, IValue> getParameters() {
return Collections.unmodifiableMap(parameters);
}
};
}
}