code: package org.applause.lang.scoping;
  
  import java.util.Arrays;
  import java.util.List;
  import java.util.NoSuchElementException;
  
  import org.applause.lang.applauseDsl.ApplauseDslFactory;
  import org.applause.lang.applauseDsl.CollectionIterator;
  import org.applause.lang.applauseDsl.CollectionLiteral;
  import org.applause.lang.applauseDsl.ComplexProviderConstruction;
  import org.applause.lang.applauseDsl.ContentProvider;
  import org.applause.lang.applauseDsl.Expression;
  import org.applause.lang.applauseDsl.ObjectReference;
  import org.applause.lang.applauseDsl.Parameter;
  import org.applause.lang.applauseDsl.Property;
  import org.applause.lang.applauseDsl.ProviderConstruction;
  import org.applause.lang.applauseDsl.ScalarExpression;
  import org.applause.lang.applauseDsl.SimpleProviderConstruction;
  import org.applause.lang.applauseDsl.SimpleType;
  import org.applause.lang.applauseDsl.StringConcat;
  import org.applause.lang.applauseDsl.StringFunction;
  import org.applause.lang.applauseDsl.StringLiteral;
  import org.applause.lang.applauseDsl.StringReplace;
  import org.applause.lang.applauseDsl.StringSplit;
  import org.applause.lang.applauseDsl.StringUrlConform;
  import org.applause.lang.applauseDsl.Type;
  import org.applause.lang.applauseDsl.TypeDescription;
  import org.applause.lang.applauseDsl.VariableDeclaration;
  import org.applause.lang.applauseDsl.util.ApplauseDslSwitch;
  import org.eclipse.emf.ecore.EObject;
  import org.eclipse.emf.ecore.util.EcoreUtil;
  import org.eclipse.xtext.EcoreUtil2;
  
  import com.google.common.base.Predicate;
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.Iterables;
  
  public class TypeUtil {
  	
  //	public class TypeDesc {
  //		private final Type type;
  //		private final boolean many;
  //		
  //		public TypeDesc(Type type, boolean many) {
  //			this.type = type;
  //			this.many = many;
  //		}
  //		
  //		public Type getType() {
  //			return type;
  //		}
  //		public boolean isMany() {
  //			return many;
  //		}
  //	}
  
  	private static ApplauseDslSwitch<TypeDescription> typeOf = new ApplauseDslSwitch<TypeDescription>() {
  		public TypeDescription caseProperty(Property object) {
  			return object.getDescription();
  		};
  		
  		public TypeDescription caseVariableDeclaration(VariableDeclaration object) {
  			return null;
  		};
  
  		public TypeDescription caseParameter(Parameter object) {
  			return object.getDescription();
  		};
  
  		public TypeDescription caseCollectionIterator(CollectionIterator object) {
  			return createDesc(doGetTypeOf(object.getCollection()).getType(), false);
  		};
  
  		public TypeDescription caseObjectReference(ObjectReference object) {
  			while (object.getTail() != null)
  				object = object.getTail();
  
  			return doGetTypeOf(object.getObject());
  		};
  		
  		public Type getStringType(EObject object) {
  			EObject model = EcoreUtil.getRootContainer(object);
  			List<SimpleType> allSimpleTypes = EcoreUtil2.getAllContentsOfType(model, SimpleType.class);
  			Predicate<SimpleType> stringTypePredicate = new Predicate<SimpleType>() {
  				public boolean apply(SimpleType input) {
  					return "String".equals(input.getName());
  				}
  			};
  
  			try {
  				return Iterables.getOnlyElement(Iterables.filter(allSimpleTypes, stringTypePredicate));
  			} catch(NoSuchElementException ex) {
  				return null;
  			}
  		}
  		
  		public TypeDescription caseStringLiteral(StringLiteral object) {
  			return createStringDesc(object);
  		}
  		
  		public TypeDescription caseStringFunction(StringFunction object) {
  			return createStringDesc(object);
  		};
  		
  		public TypeDescription caseStringSplit(StringSplit object) {
  			return createDesc(getStringType(object), true);
  		};
  		
  		private TypeDescription createStringDesc(EObject object) {
  			return createDesc(getStringType(object), false);
  		};
  
  		private TypeDescription createDesc(Type type, boolean isMany) {
  			TypeDescription result = ApplauseDslFactory.eINSTANCE.createTypeDescription();
  			result.setMany(isMany);
  			result.setType(type);
  			return result;
  		};
  		
  		public TypeDescription caseCollectionLiteral(CollectionLiteral object) {
  			return createDesc(doGetTypeOf(object.getItems().get(0)).getType(), true);
  		};
  		
  		public TypeDescription caseComplexProviderConstruction(ComplexProviderConstruction object) {
  			ContentProvider p = object.getProvider();
  			if(p==null)
  				return null;
  			
  			return createDesc(p.getType(), p.isMany());
  		};
  		
  		public TypeDescription caseSimpleProviderConstruction(SimpleProviderConstruction object) {
  			return doGetTypeOf(object.getExpression());
  		};
  		
  		
  	};
  	
  	private static TypeDescription doGetTypeOf(EObject object) {
  //		System.out.println("doGetTypeOf: " + object.eClass().getName());
  		TypeDescription result = typeOf.doSwitch(object);
  		if(result == null) {
  			typeOf.doSwitch(object);
  		}
  		return result;
  	}
  	
  	public static TypeDescription getTypeOf(ScalarExpression expression) {
  		return doGetTypeOf(expression);
  	}
  	
  	public static TypeDescription getTypeOf(VariableDeclaration declaration) {
  		return doGetTypeOf(declaration);
  	}
  	
  	public static TypeDescription getTypeOf(Expression expression) {
  		return doGetTypeOf(expression);
  	}
  	
  	public static TypeDescription getTypeOf(ProviderConstruction construction) {
  		return doGetTypeOf(construction);
  	}
  	
  	public static ApplauseDslSwitch<Iterable<ObjectReference>> referencesIn = new ApplauseDslSwitch<Iterable<ObjectReference>>() {
  		public Iterable<ObjectReference> caseScalarExpression(ScalarExpression object) {
  			return Iterables.emptyIterable();
  		};
  		
  		public Iterable<ObjectReference> caseObjectReference(ObjectReference object) {
  			return Arrays.asList(object);
  		};
  		
  		public Iterable<ObjectReference> caseStringConcat(StringConcat sc) {
  			Iterable<ObjectReference> result = Iterables.emptyIterable();
  			for(ScalarExpression e : sc.getValues()) {
  				result = Iterables.concat(result, getReferencesIn(e));
  			}
  			return result;
  		};
  		
  		public Iterable<ObjectReference> caseStringUrlConform(StringUrlConform object) {
  			return getReferencesIn(object.getValue());
  		};
  		
  		@SuppressWarnings("unchecked")
  		public Iterable<ObjectReference> caseStringReplace(StringReplace object) {
  			return Iterables.concat(
  					getReferencesIn(object.getValue()),
  					getReferencesIn(object.getMatch()),
  					getReferencesIn(object.getReplacement())
  					);
  		};
  		
  		
  	};
  
  	public static List<ObjectReference> getReferencesIn(ScalarExpression e) {
  		return ImmutableList.copyOf(referencesIn.doSwitch(e));
  	}
  	
  	
  
  	public static boolean isAssignable(TypeDescription target,
  			TypeDescription value) {
  		if(target == null || value == null)
  			return false;
  		return isAssignable(target.getType(), value.getType()) && (target.isMany() == value.isMany());  
  	}
  
  	private static boolean isAssignable(Type target, Type value) {
  		if(target == null || value == null)
  			return false;
  		
  		// look at type hierarchy
  		return target == value;
  	}
  
  	public static String asReadableString(TypeDescription desc) {
  		if(desc==null || desc.getType() == null)
  			return "unknown";
  
  		return desc.getType().getName() + (desc.isMany()?"[]":"");
  	}
  
  }
  