/*******************************************************************************
* Copyright (c) 2009 Centrum Wiskunde en Informatica (CWI)
* 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:
* Arnold Lankamp - interfaces and implementation
*******************************************************************************/
package org.rascalmpl.value.impl.fast;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.rascalmpl.value.IAnnotatable;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IWithKeywordParameters;
import org.rascalmpl.value.exceptions.FactTypeUseException;
import org.rascalmpl.value.impl.AbstractDefaultAnnotatable;
import org.rascalmpl.value.impl.AbstractDefaultWithKeywordParameters;
import org.rascalmpl.value.impl.AbstractValue;
import org.rascalmpl.value.impl.AnnotatedConstructorFacade;
import org.rascalmpl.value.impl.ConstructorWithKeywordParametersFacade;
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 IConstructor.
* <br /><br />
* Constructors that are annotated will use the AnnotatedConstructor class instead.
*
* @author Arnold Lankamp
* @author Jurgen Vinju (specialisations)
*/
/*package*/ class Constructor {
private static abstract class AbstractConstructor extends AbstractValue implements IConstructor {
protected final Type constructorType;
private int hashCode;
public AbstractConstructor(Type constructorType) {
this.constructorType = constructorType;
}
@Override
public IConstructor set(String label, IValue arg) throws FactTypeUseException {
return set(constructorType.getFieldIndex(label), arg);
}
@Override
public Iterable<IValue> getChildren(){
return this;
}
@Override
public boolean isAnnotatable() {
return true;
}
@Override
public boolean declaresAnnotation(TypeStore store, String label) {
return (store.getAnnotationType(constructorType.getAbstractDataType(), label) != null);
}
@Override
public IConstructor replace(int first, int second, int end, IList repl)
throws FactTypeUseException, IndexOutOfBoundsException {
throw new UnsupportedOperationException("Replace not supported on constructor.");
}
@Override
public IAnnotatable<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 isEqual(IValue value){
if(value == this) return true;
if(value == null) return false;
if (value instanceof IConstructor){
IConstructor otherTree = (IConstructor) value;
if(!constructorType.comparable(otherTree.getConstructorType())) {
return false;
}
final Iterator<IValue> it1 = this.iterator();
final Iterator<IValue> it2 = otherTree.iterator();
while (it1.hasNext() && it2.hasNext()) {
// call to IValue.isEqual(IValue)
if (it1.next().isEqual(it2.next()) == false) {
return false;
}
}
// if this has keyword parameters, then isEqual is overriden by the wrapper
// but if the other has keyword parameters, then we should fail here:
return otherTree.mayHaveKeywordParameters() ? !otherTree.asWithKeywordParameters().hasParameters() : true;
}
return false;
}
@Override
public int hashCode(){
if (hashCode == 0) {
hashCode = constructorType.hashCode();
for (int i = arity() - 1; i >= 0; i--) {
hashCode = (hashCode << 23) + (hashCode >> 5);
hashCode ^= get(i).hashCode();
}
}
return hashCode;
}
@Override
public boolean equals(Object o){
if(o == this) return true;
if(o == null) return false;
if(o.getClass() == getClass()){
AbstractConstructor otherTree = (AbstractConstructor) o;
if (constructorType != otherTree.constructorType) {
return false;
}
Iterator<IValue> children = iterator();
Iterator<IValue> other = otherTree.iterator();
while (children.hasNext() && other.hasNext()) {
if (!children.next().equals(other.next())) {
return false;
}
}
return true;
}
return false;
}
@Override
public boolean has(String label) {
return getConstructorType().hasField(label);
}
@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 false;
}
@Override
public java.util.Set<String> getParameterNames() {
return Collections.emptySet();
}
@Override
public Map<String, IValue> getParameters() {
return Collections.unmodifiableMap(parameters);
}
};
}
@Override
public IValue get(String label){
return get(constructorType.getFieldIndex(label));
}
@Override
public String getName(){
return constructorType.getName();
}
@Override
public Type getUninstantiatedConstructorType() {
return constructorType;
}
@Override
public Type getType(){
return getConstructorType().getAbstractDataType();
}
@Override
public Iterator<IValue> iterator() {
return new Iterator<IValue>() {
private final int max = arity();
private int cur = 0;
@Override
public boolean hasNext() {
return cur != max;
}
@Override
public IValue next() {
IValue res = get(cur);
cur++;
return res;
}
};
}
@Override
public Type getConstructorType(){
if (constructorType.getAbstractDataType().isParameterized()) {
// this assures we always have the most concrete type for constructors.
Type[] actualTypes = new Type[constructorType.getArity()];
int i = 0;
for (IValue child : this) {
actualTypes[i++] = child.getType();
}
Map<Type,Type> bindings = new HashMap<Type,Type>();
constructorType.getFieldTypes().match(TypeFactory.getInstance().tupleType(actualTypes), bindings);
for (Type field : constructorType.getAbstractDataType().getTypeParameters()) {
if (!bindings.containsKey(field)) {
bindings.put(field, TypeFactory.getInstance().voidType());
}
}
return constructorType.instantiate(bindings);
}
return constructorType;
}
@Override
public Type getChildrenTypes(){
return constructorType.getFieldTypes();
}
@Override
public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E{
return v.visitConstructor(this);
}
}
private static class Constructor0 extends AbstractConstructor {
public Constructor0(Type constructorType) {
super(constructorType);
}
@Override
public int arity() {
return 0;
}
@Override
public Iterator<IValue> iterator() {
return Collections.<IValue>emptyList().iterator();
}
@Override
public int hashCode(){
return constructorType.hashCode();
}
@Override
public IValue get(int arg0) throws IndexOutOfBoundsException {
throw new IndexOutOfBoundsException();
}
@Override
public IConstructor set(int arg0, IValue arg1) throws FactTypeUseException {
throw new IndexOutOfBoundsException();
}
@Override
public IConstructor set(String arg0, IValue arg1) throws FactTypeUseException {
throw new IndexOutOfBoundsException();
}
}
private static class Constructor1 extends AbstractConstructor {
private final IValue arg1;
public Constructor1(Type constructorType, IValue arg1) {
super(constructorType);
this.arg1 = arg1;
}
@Override
public int arity() {
return 1;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor1(constructorType, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class Constructor2 extends AbstractConstructor {
private final IValue arg1;
private final IValue arg2;
public Constructor2(Type constructorType, IValue arg1, IValue arg2) {
super(constructorType);
this.arg1 = arg1;
this.arg2 = arg2;
}
@Override
public int arity() {
return 2;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
case 1: return arg2;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor2(constructorType, newArg, arg2);
case 1: return new Constructor2(constructorType, arg1, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class Constructor3 extends AbstractConstructor {
private final IValue arg1;
private final IValue arg2;
private final IValue arg3;
public Constructor3(Type constructorType, IValue arg1, IValue arg2, IValue arg3) {
super(constructorType);
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
}
@Override
public int arity() {
return 3;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
case 1: return arg2;
case 2: return arg3;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor3(constructorType, newArg, arg2, arg3);
case 1: return new Constructor3(constructorType, arg1, newArg, arg3);
case 2: return new Constructor3(constructorType, arg1, arg2, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class Constructor4 extends AbstractConstructor {
private final IValue arg1;
private final IValue arg2;
private final IValue arg3;
private final IValue arg4;
public Constructor4(Type constructorType, IValue arg1, IValue arg2, IValue arg3, IValue arg4) {
super(constructorType);
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
this.arg4 = arg4;
}
@Override
public int arity() {
return 4;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
case 1: return arg2;
case 2: return arg3;
case 3: return arg4;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor4(constructorType, newArg, arg2, arg3, arg4);
case 1: return new Constructor4(constructorType, arg1, newArg, arg3, arg4);
case 2: return new Constructor4(constructorType, arg1, arg2, newArg, arg4);
case 3: return new Constructor4(constructorType, arg1, arg2, arg3, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class Constructor5 extends AbstractConstructor {
private final IValue arg1;
private final IValue arg2;
private final IValue arg3;
private final IValue arg4;
private final IValue arg5;
public Constructor5(Type constructorType, IValue arg1, IValue arg2, IValue arg3, IValue arg4, IValue arg5) {
super(constructorType);
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
this.arg4 = arg4;
this.arg5 = arg5;
}
@Override
public int arity() {
return 5;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
case 1: return arg2;
case 2: return arg3;
case 3: return arg4;
case 4: return arg5;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor5(constructorType, newArg, arg2, arg3, arg4, arg5);
case 1: return new Constructor5(constructorType, arg1, newArg, arg3, arg4, arg5);
case 2: return new Constructor5(constructorType, arg1, arg2, newArg, arg4, arg5);
case 3: return new Constructor5(constructorType, arg1, arg2, arg3, newArg, arg5);
case 4: return new Constructor5(constructorType, arg1, arg2, arg3, arg4, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class Constructor6 extends AbstractConstructor {
private final IValue arg1;
private final IValue arg2;
private final IValue arg3;
private final IValue arg4;
private final IValue arg5;
private final IValue arg6;
public Constructor6(Type constructorType, IValue arg1, IValue arg2, IValue arg3, IValue arg4, IValue arg5, IValue arg6) {
super(constructorType);
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
this.arg4 = arg4;
this.arg5 = arg5;
this.arg6 = arg6;
}
@Override
public int arity() {
return 6;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
case 1: return arg2;
case 2: return arg3;
case 3: return arg4;
case 4: return arg5;
case 5: return arg6;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor6(constructorType, newArg, arg2, arg3, arg4, arg5, arg6);
case 1: return new Constructor6(constructorType, arg1, newArg, arg3, arg4, arg5, arg6);
case 2: return new Constructor6(constructorType, arg1, arg2, newArg, arg4, arg5, arg6);
case 3: return new Constructor6(constructorType, arg1, arg2, arg3, newArg, arg5, arg6);
case 4: return new Constructor6(constructorType, arg1, arg2, arg3, arg4, newArg, arg6);
case 5: return new Constructor6(constructorType, arg1, arg2, arg3, arg4, arg5, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class Constructor7 extends AbstractConstructor {
private final IValue arg1;
private final IValue arg2;
private final IValue arg3;
private final IValue arg4;
private final IValue arg5;
private final IValue arg6;
private final IValue arg7;
public Constructor7(Type constructorType, IValue arg1, IValue arg2, IValue arg3, IValue arg4, IValue arg5, IValue arg6, IValue arg7) {
super(constructorType);
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
this.arg4 = arg4;
this.arg5 = arg5;
this.arg6 = arg6;
this.arg7 = arg7;
}
@Override
public int arity() {
return 7;
}
@Override
public IValue get(int index) throws IndexOutOfBoundsException {
switch (index) {
case 0: return arg1;
case 1: return arg2;
case 2: return arg3;
case 3: return arg4;
case 4: return arg5;
case 5: return arg6;
case 6: return arg7;
default:
throw new IndexOutOfBoundsException();
}
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
switch (index) {
case 0: return new Constructor7(constructorType, newArg, arg2, arg3, arg4, arg5, arg6, arg7);
case 1: return new Constructor7(constructorType, arg1, newArg, arg3, arg4, arg5, arg6, arg7);
case 2: return new Constructor7(constructorType, arg1, arg2, newArg, arg4, arg5, arg6, arg7);
case 3: return new Constructor7(constructorType, arg1, arg2, arg3, newArg, arg5, arg6, arg7);
case 4: return new Constructor7(constructorType, arg1, arg2, arg3, arg4, newArg, arg6, arg7);
case 5: return new Constructor7(constructorType, arg1, arg2, arg3, arg4, arg5, newArg, arg7);
case 6: return new Constructor7(constructorType, arg1, arg2, arg3, arg4, arg5, arg6, newArg);
default:
throw new IndexOutOfBoundsException();
}
}
}
private static class ConstructorN extends AbstractConstructor {
protected final IValue[] children;
public ConstructorN(Type constructorType, IValue[] children) {
super(constructorType);
this.children = children;
}
@Override
public int arity(){
return children.length;
}
@Override
public Iterator<IValue> iterator() {
return Arrays.stream(children).iterator();
}
@Override
public IValue get(int i){
return children[i];
}
@Override
public IConstructor set(int index, IValue newArg) throws FactTypeUseException {
IValue[] newChildren = new IValue[children.length];
System.arraycopy(children, 0, newChildren, 0, children.length);
newChildren[index] = newArg;
return new ConstructorN(constructorType, newChildren);
}
}
/*package*/ static IConstructor newConstructor(Type constructorType, IValue[] children) {
switch (children.length) {
case 0: return new Constructor0(constructorType);
case 1: return new Constructor1(constructorType, children[0]);
case 2: return new Constructor2(constructorType, children[0], children[1]);
case 3: return new Constructor3(constructorType, children[0], children[1], children[2]);
case 4: return new Constructor4(constructorType, children[0], children[1], children[2], children[3]);
case 5: return new Constructor5(constructorType, children[0], children[1], children[2], children[3], children[4]);
case 6: return new Constructor6(constructorType, children[0], children[1], children[2], children[3], children[4], children[5]);
case 7: return new Constructor7(constructorType, children[0], children[1], children[2], children[3], children[4], children[5], children[6]);
default: return new ConstructorN(constructorType, children);
}
}
/*package*/ static IConstructor newConstructor(Type constructorType, IValue[] children, Map<String,IValue> kwParams) {
IConstructor r = newConstructor(constructorType, children);
if (kwParams != null && !kwParams.isEmpty()) {
return r.asWithKeywordParameters().setParameters(kwParams);
}
return r;
}
}