Package com.redhat.ceylon.compiler.java.runtime.model

Source Code of com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor$Union

package com.redhat.ceylon.compiler.java.runtime.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import ceylon.language.Anything;
import ceylon.language.AssertionError;
import ceylon.language.Basic;
import ceylon.language.Identifiable;
import ceylon.language.Null;
import ceylon.language.null_;

import com.redhat.ceylon.compiler.java.language.BooleanArray;
import com.redhat.ceylon.compiler.java.language.ByteArray;
import com.redhat.ceylon.compiler.java.language.CharArray;
import com.redhat.ceylon.compiler.java.language.DoubleArray;
import com.redhat.ceylon.compiler.java.language.FloatArray;
import com.redhat.ceylon.compiler.java.language.IntArray;
import com.redhat.ceylon.compiler.java.language.LongArray;
import com.redhat.ceylon.compiler.java.language.ObjectArray;
import com.redhat.ceylon.compiler.java.language.ShortArray;
import com.redhat.ceylon.compiler.java.metadata.Variance;
import com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel;
import com.redhat.ceylon.compiler.loader.ModelLoader.DeclarationType;
import com.redhat.ceylon.compiler.loader.model.FunctionOrValueInterface;
import com.redhat.ceylon.compiler.loader.model.LocalDeclarationContainer;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.IntersectionType;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.NothingType;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.SiteVariance;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.UnionType;
import com.redhat.ceylon.compiler.typechecker.model.Unit;
import com.redhat.ceylon.compiler.typechecker.model.Util;

public abstract class TypeDescriptor {

    public static final TypeDescriptor NothingType = new Nothing();
    private static final Variance[] NO_VARIANCE = new Variance[0];

    //
    // Methods

    public abstract ProducedType toProducedType(RuntimeModuleManager moduleManager);
   
    public abstract java.lang.Class<?> getArrayElementClass();
   
    public abstract boolean containsNull();
   
    public abstract boolean equals(Object other);
   
    public abstract int hashCode();
   
    public abstract String toString();
   
    protected abstract void stringTo(StringBuilder sb);
   
    /**
     * Returns a hashcode for the given elements
     * computed so that the order of the elements doesn't matter
     */
    static int unorderedHashCode(TypeDescriptor[] array) {
        int hash = 0;
        for (int i = 0; i < array.length; i++) {
            hash ^= array[i].hashCode();
        }
        return hash;
    }
 

    //
    // Subtypes

    public static interface QualifiableTypeDescriptor {
        public ProducedType toProducedType(ProducedType qualifyingType, RuntimeModuleManager moduleManager);
    }
   
    public static abstract class Generic extends TypeDescriptor {
        protected final TypeDescriptor[] typeArguments;
        protected final Variance[] useSiteVariance;

        public Generic(Variance[] useSiteVariance, TypeDescriptor[] typeArguments){
            this.typeArguments = typeArguments;
            this.useSiteVariance = useSiteVariance;
        }

        public TypeDescriptor[] getTypeArguments() {
            return typeArguments;
        }

        protected boolean equals(Generic other) {
            return Arrays.equals(typeArguments, other.typeArguments)
                    && Arrays.equals(useSiteVariance, other.useSiteVariance);
        }
       
        protected void toString(StringBuilder b) {
            if(typeArguments.length > 0){
                b.append("<");
                for(int i=0;i<typeArguments.length;i++){
                    if(i>0)
                        b.append(",");
                    b.append(typeArguments[i]);
                }
                b.append(">");
            }
        }
       
        protected void stringTo(StringBuilder b) {
            if(typeArguments.length > 0){
                b.append("<");
                for(int i=0;i<typeArguments.length;i++){
                    if(i>0)
                        b.append(",");
                    typeArguments[i].stringTo(b);
                }
                b.append(">");
            }
        }
       
        protected ProducedType applyUseSiteVariance(TypeDeclaration decl, ProducedType type) {
            // apply use site variance if required
            if(useSiteVariance.length != 0){
                List<TypeParameter> typeParameters = decl.getTypeParameters();
                int i = 0;
                for(TypeParameter typeParameter : typeParameters){
                    // bail if we have more type parameters than provided use site variance
                    if(i >= useSiteVariance.length)
                        break;
                    switch(useSiteVariance[i]){
                    case IN:
                        type.setVariance(typeParameter, SiteVariance.IN);
                        break;
                    case OUT:
                        type.setVariance(typeParameter, SiteVariance.OUT);
                        break;
                    case NONE:
                    default:
                        break;
                    }
                    i++;
                }
            }
            return type;
        }
    }

    public static class Class extends Generic implements QualifiableTypeDescriptor {
        private java.lang.Class<?> klass;

        public Class(java.lang.Class<?> klass, Variance[] useSiteVariance, TypeDescriptor[] typeArguments){
            super(useSiteVariance, typeArguments);
            this.klass = klass;
        }

        public java.lang.Class<?> getKlass() {
            return klass;
        }

        @Override
        public boolean equals(Object obj) {
            if(this == obj)
                return true;
            if(obj == null || obj instanceof Class == false)
                return false;
            Class other = (Class) obj;
            if(klass != other.klass)
                return false;
            // now compare type arguments
            return super.equals(other);
        }
       
        @Override
        public int hashCode() {
            int ret = 17;
            ret = 37 * ret + "class".hashCode();
            ret = 37 * ret + Arrays.hashCode(typeArguments);
            ret = 37 * ret + Arrays.hashCode(useSiteVariance);
            ret = 37 * ret + klass.hashCode();
            return  ret;
        }
       
        @Override
        public String toString() {
            String className = klass.getName();
            if (typeArguments.length != 0) {
                StringBuilder b = new StringBuilder(className);
                // add type arguments
                super.stringTo(b);
                return b.toString();
            } else {
                return className;
            }
        }
       
        @Override
        protected void stringTo(StringBuilder sb) {
            String className = klass.getName();
            sb.append(className);
            if (typeArguments.length != 0) {
                // add type arguments
                super.stringTo(sb);
            }
        }

        @Override
        public ProducedType toProducedType(RuntimeModuleManager moduleManager){
            return toProducedType(null, moduleManager);
        }
       
        @Override
        public ProducedType toProducedType(ProducedType qualifyingType, RuntimeModuleManager moduleManager){
            // FIXME: is this really enough?
            String typeName = klass.getName();
            Module module = moduleManager.findModuleForClass(klass);
            TypeDeclaration decl = (TypeDeclaration) moduleManager.getModelLoader().getDeclaration(module, typeName, DeclarationType.TYPE);
            List<ProducedType> typeArgs = new ArrayList<ProducedType>(typeArguments.length);
            for(TypeDescriptor typeArg : typeArguments){
                typeArgs.add(Metamodel.getProducedType(typeArg));
            }
            ProducedType type = decl.getProducedType(qualifyingType, typeArgs);
            type = applyUseSiteVariance(decl, type);
            return type;
        }

        @Override
        public java.lang.Class<?> getArrayElementClass() {
            if (klass==Null.class ||
                klass==ceylon.language.Object.class ||
                klass==Anything.class ||
                klass==Basic.class ||
                klass==Identifiable.class) {
                return java.lang.Object.class;
            }
            if (klass==ceylon.language.Exception.class ||
                klass==ceylon.language.Throwable.class) {
                return java.lang.Throwable.class;
            }
            if (klass==ObjectArray.class) {
                return java.lang.Object[].class;
            }
            if (klass==BooleanArray.class) {
                return boolean[].class;
            }
            if (klass==LongArray.class) {
                return long[].class;
            }
            if (klass==IntArray.class) {
                return int[].class;
            }
            if (klass==ShortArray.class) {
                return short[].class;
            }
            if (klass==ByteArray.class) {
                return byte[].class;
            }
            if (klass==DoubleArray.class) {
                return double[].class;
            }
            if (klass==FloatArray.class) {
                return float[].class;
            }
            if (klass==CharArray.class) {
                return char[].class;
            }
            return klass;
        }

        @Override
        public boolean containsNull() {
            return klass==Null.class || klass == null_.class || klass==Anything.class;
        }
    }

    public static class FunctionOrValue extends Generic implements QualifiableTypeDescriptor {

        private final String name;
        private final java.lang.Class<?> klass;
        private final boolean local;
       
        /**
         * For members
         */
        public FunctionOrValue(String name, TypeDescriptor[] typeArguments) {
            // we can't have use-site variance for local types in functions, since they are only visible inside their declaration
            // not where they are used
            super(NO_VARIANCE, typeArguments);
            this.klass = null;
            this.name = name;
            if(name.isEmpty())
                local = false;
            else
                local = Character.isDigit(name.charAt(0));
        }

        /**
         * For toplevels
         */
        public FunctionOrValue(java.lang.Class<?> klass, TypeDescriptor[] typeArguments) {
            // we can't have use-site variance for local types in functions, since they are only visible inside their declaration
            // not where they are used
            super(NO_VARIANCE, typeArguments);
            this.klass = klass;
            this.name = null;
            this.local = false;
        }

        @Override
        public ProducedType toProducedType(RuntimeModuleManager moduleManager) {
            // FIXME: is this really enough?
            String typeName = klass.getName();
            // use the toplevel wrapper declaration
            Module module = moduleManager.findModuleForClass(klass);
            // FIXME: this is confuses setters and getters, but this should not matter since we only care about container
            // functions and their type arguments
            TypedDeclaration declaration = (TypedDeclaration) moduleManager.getModelLoader().getDeclaration(module, typeName, DeclarationType.TYPE);
            return makeProducedType(null, declaration, moduleManager);
        }

        @Override
        public ProducedType toProducedType(ProducedType qualifyingType, RuntimeModuleManager moduleManager) {
            // need to find the local Declaration in its container
            Declaration qualifyingDeclaration = qualifyingType.getDeclaration();
            if(qualifyingDeclaration instanceof FunctionOrValueInterface)
                qualifyingDeclaration = ((FunctionOrValueInterface) qualifyingDeclaration).getUnderlyingDeclaration();
            // FIXME: this is confuses setters and getters, but this should not matter since we only care about container
            // functions and their type arguments
            TypedDeclaration declaration;
            if(local)
                declaration = (TypedDeclaration) ((LocalDeclarationContainer)qualifyingDeclaration).getLocalDeclaration(name);
            else
                declaration = (TypedDeclaration) qualifyingDeclaration.getDirectMember(name, null, false);
            return makeProducedType(qualifyingType, declaration, moduleManager);
        }

        private ProducedType makeProducedType(ProducedType qualifyingType, TypedDeclaration declaration, RuntimeModuleManager moduleManager) {
            // add the type args
            List<ProducedType> typeArgs = new ArrayList<ProducedType>(typeArguments.length);
            for(TypeDescriptor typeArg : typeArguments){
                typeArgs.add(Metamodel.getProducedType(typeArg));
            }
            // wrap it
            return new FunctionOrValueInterface(declaration).getProducedType(qualifyingType, typeArgs);
        }

        @Override
        public java.lang.Class<?> getArrayElementClass() {
            throw new AssertionError("Should never be called");
        }

        @Override
        public boolean containsNull() {
            throw new AssertionError("Should never be called");
        }

        @Override
        public boolean equals(Object obj) {
            if(this == obj)
                return true;
            if(obj == null || obj instanceof FunctionOrValue == false)
                return false;
            FunctionOrValue other = (FunctionOrValue) obj;
            if(name != null){
                if(!name.equals(other.name))
                    return false;
            }else{
                if(klass != other.klass)
                    return false;
            }
            // now compare type arguments
            return super.equals(other);
        }
       
        @Override
        public int hashCode() {
            int ret = 17;
            ret = 37 * ret + "functionorvalue".hashCode();
            ret = 37 * ret + Arrays.hashCode(typeArguments);
            ret = 37 * ret + (klass != null ? klass.hashCode() : 0);
            ret = 37 * ret + (name != null ? name.hashCode() : 0);
            return  ret;
        }
       
        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            b.append(name);
            // add type arguments
            super.toString(b);
            return b.toString();
        }
    }
   
    public static class Member extends TypeDescriptor {

        private TypeDescriptor container;
        private TypeDescriptor member;

        public Member(TypeDescriptor container, TypeDescriptor member) {
            this.member = member;
            this.container = container;
        }

        public TypeDescriptor getContainer() {
            return container;
        }

        public TypeDescriptor getMember() {
            return member;
        }
       
        @Override
        public boolean equals(Object obj) {
            if(this == obj)
                return true;
            if(obj == null || obj instanceof Member == false)
                return false;
            Member other = (Member) obj;
            return container.equals(other.container)
                    && member.equals(other.member);
        }

        @Override
        public int hashCode() {
            int ret = 17;
            ret = 37 * ret + "member".hashCode();
            ret = 37 * ret + container.hashCode();
            ret = 37 * ret + member.hashCode();
            return ret;
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            stringTo(b);
            return b.toString();
        }
       
        @Override
        public void stringTo(StringBuilder sb) {
            sb.append(container);
            sb.append(".");
            sb.append(member);
        }

        @Override
        public ProducedType toProducedType(RuntimeModuleManager moduleManager) {
            ProducedType qualifyingType = Metamodel.getProducedType(container);
            return ((QualifiableTypeDescriptor)member).toProducedType(qualifyingType, moduleManager);
        }

    @Override
        public java.lang.Class<?> getArrayElementClass() {
          return member.getArrayElementClass();
        }

    @Override
        public boolean containsNull() {
          return false;
        }
    }
   
    private static class Nothing extends TypeDescriptor {
        @Override
        public boolean equals(Object obj) {
            return obj == this;
        }
       
        @Override
        public int hashCode() {
            int ret = 17;
            ret = 37 * ret + "nothing".hashCode();
            return ret;
        }

        @Override
        public ProducedType toProducedType(RuntimeModuleManager moduleManager) {
            return new NothingType(moduleManager.getModelLoader().getUnit()).getType();
        }
       
        @Override
        public String toString() {
            return "ceylon.language.Nothing";
        }
       
        @Override
        public void stringTo(StringBuilder sb) {
            sb.append("ceylon.language.Nothing");
        }
       
    @Override
        public java.lang.Class<?> getArrayElementClass() {
          return java.lang.Object.class;
        }

    @Override
        public boolean containsNull() {
          return false;
        }
    }
   
    private abstract static class Composite extends TypeDescriptor {
        protected final TypeDescriptor[] members;

        public Composite(TypeDescriptor[] members) {
            this.members = members;
        }

        public TypeDescriptor[] getMembers() {
            return members;
        }

        protected boolean equals(Composite other) {
            return allContained(members, other.members)
                    && allContained(other.members, members);
        }

        private static boolean allContained(TypeDescriptor[] a, TypeDescriptor[] b) {
            OUTER:
            for(int i=0;i<a.length;i++){
                TypeDescriptor ref = a[i];
                // find it anywhere
                for(int j=0;j<b.length;j++){
                    if(ref.equals(b[j]))
                        continue OUTER;
                }
                // not found
                return false;
            }
            return true;
        }

        protected String toString(char sep) {
            StringBuilder b = new StringBuilder();
            if(members.length > 0){
                for(int i=0;i<members.length;i++){
                    if(i>0)
                        b.append(sep);
                    b.append(members[i]);
                }
            }
            return b.toString();
        }
        protected void stringTo(StringBuilder sb, char sep) {
           
            if(members.length > 0){
                for(int i=0;i<members.length;i++){
                    if(i>0)
                        sb.append(sep);
                    members[i].stringTo(sb);
                }
            }
        }
    }

    public static class Union extends Composite {

        public Union(TypeDescriptor[] members) {
            super(members);
        }

        @Override
        public boolean equals(Object obj) {
            if(this == obj)
                return true;
            if(obj == null || obj instanceof Union == false)
                return false;
            return super.equals((Union)obj);
        }

        @Override
        public int hashCode() {
            int ret = 17;
            ret = 37 * ret + "union".hashCode();
            ret = 37 * ret + unorderedHashCode(members);
            return ret;
        }

        @Override
        public String toString() {
            return super.toString('|');
        }
       
        @Override
        public void stringTo(StringBuilder sb) {
            super.stringTo(sb, '|');
        }

        @Override
        public ProducedType toProducedType(RuntimeModuleManager moduleManager) {
            UnionType ret = new UnionType(moduleManager.getModelLoader().getUnit());
            ArrayList<ProducedType> caseTypes = new ArrayList<ProducedType>(members.length);
            for(TypeDescriptor member : members)
                Util.addToUnion(caseTypes,Metamodel.getProducedType(member));
            ret.setCaseTypes(caseTypes);
            return ret.getType();
        }

    @Override
        public java.lang.Class<?> getArrayElementClass() {
      java.lang.Class<?> result = null;
      for (TypeDescriptor td: members) {
            if (td instanceof Nothing ||
              td instanceof Class && td.containsNull()) {
              continue;
            }
            java.lang.Class<?> c = td.getArrayElementClass();
            if (result==null) {
              result = c;
            }
            else if (result!=c) {
              return java.lang.Object.class;
            }
          }
          return result==null ?
              java.lang.Object.class : result;
        }

    @Override
        public boolean containsNull() {
      for (TypeDescriptor td: members) {
        if (td.containsNull()) return true;
      }
          return false;
        }
   
    }

    public static class Intersection extends Composite {

        public Intersection(TypeDescriptor[] members) {
            super(members);
        }

        @Override
        public boolean equals(Object obj) {
            if(this == obj)
                return true;
            if(obj == null || obj instanceof Intersection == false)
                return false;
            return super.equals((Intersection)obj);
        }
       
        @Override
        public int hashCode() {
            int ret = 17;
            ret = 37 * ret + "intersection".hashCode();
            ret = 37 * ret + unorderedHashCode(members);
            return ret;
        }

        @Override
        public String toString() {
            return super.toString('&');
        }
       
        @Override
        public void stringTo(StringBuilder sb) {
            super.stringTo(sb, '&');
        }

        @Override
        public ProducedType toProducedType(RuntimeModuleManager moduleManager) {
            Unit unit = moduleManager.getModelLoader().getUnit();
      IntersectionType ret = new IntersectionType(unit);
            ArrayList<ProducedType> satisfiedTypes = new ArrayList<ProducedType>(members.length);
            for(TypeDescriptor member : members)
              Util.addToIntersection(satisfiedTypes, Metamodel.getProducedType(member), unit);
            ret.setSatisfiedTypes(satisfiedTypes);
            return ret.canonicalize().getType();
        }
       
    @Override
        public java.lang.Class<?> getArrayElementClass() {
      java.lang.Class<?> result = null;
      for (TypeDescriptor td: members) {
        java.lang.Class<?> c = td.getArrayElementClass();
            if (result==null) {
              result = c;
            }
            else if (result.isAssignableFrom(c)) {
              result = c;
            }
            else if (c.isAssignableFrom(result)) {
              //do nothing
            }
            else if (result!=c) {
              return java.lang.Object.class;
            }
          }
          return result==null ?
              java.lang.Object.class : result;
        }

    @Override
        public boolean containsNull() {
      for (TypeDescriptor td: members) {
        if (!td.containsNull()) return false;
      }
          return true;
        }
   
    }

    //
    // Factory methods
   
    public static TypeDescriptor member(TypeDescriptor container, TypeDescriptor member){
        return new Member(container, member);
    }
   
    public static TypeDescriptor klass(java.lang.Class<?> klass, TypeDescriptor... typeArguments) {
        // delegate
        return klass(klass, NO_VARIANCE, typeArguments);
    }

    public static TypeDescriptor klass(java.lang.Class<?> klass, Variance[] useSiteVariance, TypeDescriptor... typeArguments) {
        return new Class(klass, useSiteVariance, typeArguments);
    }

    /**
     * For members
     */
    public static TypeDescriptor functionOrValue(String name, TypeDescriptor... typeArguments) {
        return new FunctionOrValue(name, typeArguments);
    }

    /**
     * For toplevel method/attributes
     */
    public static TypeDescriptor functionOrValue(java.lang.Class<?> klass, TypeDescriptor... typeArguments) {
        return new FunctionOrValue(klass, typeArguments);
    }

    public static TypeDescriptor union(TypeDescriptor... members){
        if(members == null || members.length == 0)
            throw new AssertionError("members can't be null or empty");
        members = flattenUnionOrIntersection(members, true);
        TypeDescriptor single = getSingleTypeDescriptorIfUnique(members);
        if(single != null)
            return single;
        members = removeDuplicates(members);
        return new Union(members);
    }

    public static TypeDescriptor intersection(TypeDescriptor... members){
        if(members == null || members.length == 0)
            throw new AssertionError("members can't be null or empty");
        members = flattenUnionOrIntersection(members, false);
        TypeDescriptor single = getSingleTypeDescriptorIfUnique(members);
        if(single != null)
            return single;
        members = removeDuplicates(members);
        return new Intersection(members);
    }

    /**
     * Remove {@link Union}s or {@link Intersection}s from the given {@code members}, flattening
     * the members of those Unions/Intersections found
     * (plus the Union/Intersection members of <em>those</em>, etc) into the
     * returned array:
     * <pre>
     * A&(B&(C&D))  =>  A&B&C&D
     * A|(B|(C|D))  =>  A|B|C|D
     * </pre>
     * @param members The members of the union or intersection.
     * @param union true to flatten Union within the members, false to flatten intersections.
     */
    private static TypeDescriptor[] flattenUnionOrIntersection(TypeDescriptor[] members, boolean union) {
        TypeDescriptor[] iterating = members;
        for (int ii = 0; ii < iterating.length; ii++) {
            TypeDescriptor td = iterating[ii];
            if (td instanceof Union && union
                    || td instanceof Intersection && !union) {
                TypeDescriptor[] extra = ((Composite)td).members;
                TypeDescriptor[] n = new TypeDescriptor[iterating.length -1 + extra.length];
                System.arraycopy(iterating, 0, n, 0, ii);
                if (ii + 1 < iterating.length) {
                    System.arraycopy(iterating, ii+1, n, ii, iterating.length-ii-1);
                }
                System.arraycopy(extra, 0, n, iterating.length-1, extra.length);
                iterating = n;
            }
        }
        return iterating;
    }

    private static TypeDescriptor[] removeDuplicates(TypeDescriptor[] members) {
        int duplicates = 0;
        for(int i=0;i<members.length;i++){
            TypeDescriptor ref = members[i];
            for(int j=i+1;j<members.length;j++){
                if(ref.equals(members[j])){
                    duplicates++;
                    // next ref
                    break;
                }
            }
        }
        if(duplicates > 0){
            TypeDescriptor[] unique = new TypeDescriptor[members.length-duplicates];
            REF:
            for(int i=0,u=0;i<members.length;i++){
                TypeDescriptor ref = members[i];
                for(int j=i+1;j<members.length;j++){
                    if(ref.equals(members[j])){
                        duplicates++;
                        // skip it
                        continue REF;
                    }
                }
                // it's unique: keep it
                unique[u++] = ref;
            }
            return unique;
        }
        return members;
    }

    /**
     * Returns a single type descriptor if they are all equal. Null otherwise.
     */
    private static TypeDescriptor getSingleTypeDescriptorIfUnique(TypeDescriptor[] members) {
        if(members.length == 1)
            return members[0];
        TypeDescriptor first = members[0];
        for(int i=1;i<members.length;i++){
            if(!members[i].equals(first)){
                return null;
            }
        }
        return first;
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor$Union

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.