Package com.redhat.ceylon.compiler.loader

Source Code of com.redhat.ceylon.compiler.loader.JsonPackage

package com.redhat.ceylon.compiler.loader;

import static com.redhat.ceylon.compiler.typechecker.model.Util.getSignature;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.redhat.ceylon.compiler.js.CompilerErrorException;
import com.redhat.ceylon.compiler.js.JsCompiler;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleManager;
import com.redhat.ceylon.compiler.typechecker.model.Annotation;
import com.redhat.ceylon.compiler.typechecker.model.Generic;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.NothingType;
import com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Interface;
import com.redhat.ceylon.compiler.typechecker.model.InterfaceAlias;
import com.redhat.ceylon.compiler.typechecker.model.IntersectionType;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.MethodOrValue;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.ParameterList;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.Scope;
import com.redhat.ceylon.compiler.typechecker.model.Setter;
import com.redhat.ceylon.compiler.typechecker.model.SiteVariance;
import com.redhat.ceylon.compiler.typechecker.model.TypeAlias;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.UnionType;
import com.redhat.ceylon.compiler.typechecker.model.Unit;
import com.redhat.ceylon.compiler.typechecker.model.UnknownType;
import com.redhat.ceylon.compiler.typechecker.model.Util;
import com.redhat.ceylon.compiler.typechecker.model.Value;

public class JsonPackage extends com.redhat.ceylon.compiler.typechecker.model.Package {

    //Ugly hack to have a ref to Basic at hand, to use as implicit supertype of classes
    private final static Map<String,Object> idobj = new HashMap<String, Object>();
    //This is to use as the implicit supertype of interfaces
    private final static Map<String,Object> objclass = new HashMap<String, Object>();
    //This is for type parameters
    private final static Map<String,Object> voidclass = new HashMap<String, Object>();
    private List<String> docs;
    private Map<String,Object> model;
    private final String pkgname;
    private boolean loaded = false;
    private final Unit u2 = new Unit();
    private NothingType nothing = new NothingType(u2);
    private UnknownType unknown = new UnknownType(u2);

    static {
        idobj.put(MetamodelGenerator.KEY_NAME, "Basic");
        idobj.put(MetamodelGenerator.KEY_PACKAGE, Module.LANGUAGE_MODULE_NAME);
        idobj.put(MetamodelGenerator.KEY_MODULE, Module.LANGUAGE_MODULE_NAME);
        objclass.put(MetamodelGenerator.KEY_NAME, "Object");
        objclass.put(MetamodelGenerator.KEY_PACKAGE, Module.LANGUAGE_MODULE_NAME);
        objclass.put(MetamodelGenerator.KEY_MODULE, Module.LANGUAGE_MODULE_NAME);
        voidclass.put(MetamodelGenerator.KEY_NAME, "Anything");
        voidclass.put(MetamodelGenerator.KEY_PACKAGE, Module.LANGUAGE_MODULE_NAME);
        voidclass.put(MetamodelGenerator.KEY_MODULE, Module.LANGUAGE_MODULE_NAME);
    }
    public JsonPackage(String pkgname) {
        this.pkgname = pkgname;
        setName(ModuleManager.splitModuleName(pkgname));
    }

    public void setModule(com.redhat.ceylon.compiler.typechecker.model.Module module) {
        if (module instanceof JsonModule && model == null) {
            model = ((JsonModule)module).getModelForPackage(getNameAsString());
            if (model != null) {
                docs = ((JsonModule)module).getDocs();
            }
            u2.setPackage(this);
            u2.setFilename("");
            u2.setFullPath("");
            u2.setRelativePath("");
            addUnit(u2);
        }
        super.setModule(module);
    };
    Map<String,Object> getModel() { return model; }

    void loadDeclarations() {
        if (loaded) return;
        loaded = true;
        if (getModule().getLanguageModule() == getModule() && Module.LANGUAGE_MODULE_NAME.equals(pkgname)) {
            //Mark the language module as immediately available to bypass certain validations
            getModule().setAvailable(true);
        }
        //Ugly ass hack - add Nothing to the model
        nothing.setContainer(this);
        nothing.setUnit(u2);
        setShared(model.remove("$pkg-shared") != null);
        for (Map.Entry<String,Object> e : model.entrySet()) {
            String k = e.getKey();
            if (!k.startsWith("$pkg-")) {
                @SuppressWarnings("unchecked")
                Map<String,Object> m = (Map<String,Object>)e.getValue();
                if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof String) {
                    String metatype = (String)m.get(MetamodelGenerator.KEY_METATYPE);
                    if (MetamodelGenerator.METATYPE_CLASS.equals(metatype)) {
                        refineMembers(loadClass(e.getKey(), m, this, null));
                    } else if (MetamodelGenerator.METATYPE_INTERFACE.equals(metatype)) {
                        refineMembers(loadInterface(e.getKey(), m, this, null));
                    } else if (metatype.equals(MetamodelGenerator.METATYPE_ATTRIBUTE)
                            || metatype.equals(MetamodelGenerator.METATYPE_GETTER)) {
                        loadAttribute(k, m, this, null);
                    } else if (metatype.equals(MetamodelGenerator.METATYPE_METHOD)) {
                        loadMethod(k, m, this, null);
                    } else if (metatype.equals(MetamodelGenerator.METATYPE_OBJECT)) {
                        refineMembers((com.redhat.ceylon.compiler.typechecker.model.Class)loadObject(k, m, this, null));
                    } else if (metatype.equals(MetamodelGenerator.METATYPE_ALIAS)) {
                        loadTypeAlias(k, m, this, null);
                    }
                } else if (m.get(MetamodelGenerator.KEY_METATYPE) == null) {
                    throw new IllegalArgumentException("Missing metatype from entry " + m);
                } else if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof ClassOrInterface) {
                    refineMembers((ClassOrInterface)m.get(MetamodelGenerator.KEY_METATYPE));
                }
            }
        }
    }

    /** Loads a class from the specified map. To avoid circularities, when the class is being created it is
     * added to the map, and once it's been fully loaded, all other keys are removed. */
    @SuppressWarnings("unchecked")
    com.redhat.ceylon.compiler.typechecker.model.Class loadClass(String name, Map<String, Object> m,
            Scope parent, final List<TypeParameter> existing) {
        com.redhat.ceylon.compiler.typechecker.model.Class cls;
        m.remove(MetamodelGenerator.KEY_NAME);
        if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof com.redhat.ceylon.compiler.typechecker.model.Class) {
            cls = (com.redhat.ceylon.compiler.typechecker.model.Class)m.get(MetamodelGenerator.KEY_METATYPE);
            if (m.size() <= 3) {
                //It's been fully loaded
                return cls;
            }
        } else {
            //It's not there, so create it
            if (m.containsKey("$alias")) {
                cls = new com.redhat.ceylon.compiler.typechecker.model.ClassAlias();
            } else {
                cls = new com.redhat.ceylon.compiler.typechecker.model.Class();
            }
            cls.setAbstract(m.remove("abstract") != null);
            cls.setAnonymous(m.remove("$anon") != null);
            cls.setContainer(parent);
            cls.setName(name);
            cls.setUnit(u2);
            if (parent == this) {
                u2.addDeclaration(cls);
            }
            parent.addMember(cls);
            m.put(MetamodelGenerator.KEY_METATYPE, cls);
            setAnnotations(cls, (Integer)m.remove(MetamodelGenerator.KEY_PACKED_ANNS),
                    (Map<String,Object>)m.remove(MetamodelGenerator.KEY_ANNOTATIONS));
        }
        //Type parameters are about the first thing we need to load
        final List<TypeParameter> tparms = parseTypeParameters(
                (List<Map<String,Object>>)m.remove(MetamodelGenerator.KEY_TYPE_PARAMS), cls, existing);
        final List<TypeParameter> allparms = JsonPackage.merge(tparms, existing);
        if (m.containsKey(MetamodelGenerator.KEY_SELF_TYPE)) {
            for (TypeParameter t : tparms) {
                if (t.getName().equals(m.get(MetamodelGenerator.KEY_SELF_TYPE))) {
                    cls.setSelfType(t.getType());
                }
            }
        }
        //This is to avoid circularity
        if (!(getModule().getLanguageModule()==getModule() && ("Nothing".equals(name) || "Anything".equals(name)))) {
            if (cls.getExtendedType() == null) {
                if (m.containsKey("super")) {
                    ProducedType father = getTypeFromJson((Map<String,Object>)m.get("super"),
                            parent instanceof Declaration ? (Declaration)parent : null, allparms);
                    if (father != null) {
                        m.remove("super");
                        cls.setExtendedType(father);
                    }
                } else {
                    cls.setExtendedType(getTypeFromJson(idobj,
                            parent instanceof Declaration ? (Declaration)parent : null, allparms));
                }
            }
        }

        ParameterList plist = parseParameters((List<Map<String,Object>>)m.remove(MetamodelGenerator.KEY_PARAMS),
                cls, allparms);
        plist.setNamedParametersSupported(true);
        cls.setParameterList(plist);
        if (m.containsKey("of") && cls.getCaseTypes() == null) {
            cls.setCaseTypes(parseTypeList((List<Map<String,Object>>)m.get("of"), allparms));
            m.remove("of");
        }
        if (m.containsKey(MetamodelGenerator.KEY_SATISFIES)) {
            for (ProducedType sat : parseTypeList((List<Map<String,Object>>)m.remove(MetamodelGenerator.KEY_SATISFIES), allparms)) {
                Util.addToIntersection(cls.getSatisfiedTypes(), sat, u2);
            }
        }
        if (m.containsKey(MetamodelGenerator.KEY_OBJECTS)) {
            for (Map.Entry<String,Map<String,Object>> inner : ((Map<String,Map<String,Object>>)m.remove(MetamodelGenerator.KEY_OBJECTS)).entrySet()) {
                loadObject(inner.getKey(), inner.getValue(), cls, allparms);
            }
        }
        addAttributesAndMethods(m, cls, allparms);
        return cls;
    }

    /** Parses the specified attributes and methods from JSON data and adds them to the specified declaration. */
    @SuppressWarnings("unchecked")
    private void addAttributesAndMethods(Map<String,Object> m, Declaration d, List<TypeParameter> tparms) {
        //Attributes
        Map<String, Map<String,Object>> sub = (Map<String,Map<String,Object>>)m.get(MetamodelGenerator.KEY_ATTRIBUTES);
        if (sub != null) {
            for(Map.Entry<String, Map<String,Object>> e : sub.entrySet()) {
                if (d.getDirectMember(e.getKey(), null, false) == null) {
                    d.getMembers().add(loadAttribute(e.getKey(), e.getValue(), (Scope)d, tparms));
                }
            }
        }
        //Methods
        sub = (Map<String,Map<String,Object>>)m.get(MetamodelGenerator.KEY_METHODS);
        if (sub != null) {
            for(Map.Entry<String, Map<String,Object>> e : sub.entrySet()) {
                if (d.getDirectMember(e.getKey(), null, false) == null) {
                    d.getMembers().add(loadMethod(e.getKey(), e.getValue(), (Scope)d, tparms));
                }
            }
        }
    }

    /** Creates a list of ProducedType from the references in the maps.
     * @param types A list of maps where each map is a reference to a type or type parameter.
     * @param typeParams The type parameters that can be referenced from the list of maps. */
    private List<ProducedType> parseTypeList(List<Map<String,Object>> types, List<TypeParameter> typeParams) {
        List<ProducedType> ts = new ArrayList<ProducedType>(types.size());
        for (Map<String,Object> st : types) {
            ts.add(getTypeFromJson(st, null, typeParams));
        }
        return ts;
    }

    /** Creates a list of TypeParameter from a list of maps.
     * @param typeParams The list of maps to create the TypeParameters.
     * @param container The declaration which owns the resulting type parameters.
     * @param existing A list of type parameters declared in the parent scopes which can be referenced from
     * the ones that have to be parsed. */
    private List<TypeParameter> parseTypeParameters(List<Map<String,Object>> typeParams, final Declaration container,
            List<TypeParameter> existing) {
        if (typeParams == null) return Collections.emptyList();
        //New array with existing parms to avoid modifying that one
        List<TypeParameter> allparms = new ArrayList<TypeParameter>((existing == null ? 0 : existing.size()) + typeParams.size());
        if (existing != null && !existing.isEmpty()) {
            allparms.addAll(existing);
        }
        List<TypeParameter> tparms = new ArrayList<TypeParameter>(typeParams.size());
        //To avoid circularity, this is done in two phases:
        //First create the type parameters
        for (Map<String,Object> tp : typeParams) {
            final Declaration maybe;
            if (tp.get(MetamodelGenerator.KEY_METATYPE) instanceof TypeParameter) {
                maybe = (TypeParameter)tp.get(MetamodelGenerator.KEY_METATYPE);
            } else {
                maybe = container.getDirectMember((String)tp.get(MetamodelGenerator.KEY_NAME), null, false);
            }
            if (maybe instanceof TypeParameter) {
                //we already had it (from partial loading elsewhere)
                allparms.add((TypeParameter)maybe);
                tparms.add((TypeParameter)maybe);
                tp.put(MetamodelGenerator.KEY_METATYPE, maybe);
            } else {
                TypeParameter tparm = new TypeParameter();
                tparm.setUnit(u2);
                tparm.setDeclaration(container);
                container.getMembers().add(tparm);
                if (tp.containsKey(MetamodelGenerator.KEY_NAME)) {
                    tparm.setName((String)tp.get(MetamodelGenerator.KEY_NAME));
                } else if (!tp.containsKey(MetamodelGenerator.KEY_TYPES)) {
                    throw new IllegalArgumentException("Invalid type parameter map " + tp);
                }
                String variance = (String)tp.get(MetamodelGenerator.KEY_DS_VARIANCE);
                if ("out".equals(variance)) {
                    tparm.setCovariant(true);
                } else if ("in".equals(variance)) {
                    tparm.setContravariant(true);
                }
                if (container instanceof Scope) {
                    tparm.setContainer((Scope)container);
                }
                tparm.setDefaulted(tp.containsKey(MetamodelGenerator.KEY_DEFAULT));
                tparms.add(tparm);
                allparms.add(tparm);
                tp.put(MetamodelGenerator.KEY_METATYPE, tparm);
            }
        }
        if (container instanceof TypeDeclaration) {
            ((TypeDeclaration) container).setTypeParameters(tparms);
        } else if (container instanceof Method) {
            ((Method) container).setTypeParameters(tparms);
        }
        //Second, add defaults and heritage
        for (Map<String,Object> tp : typeParams) {
            TypeParameter tparm = (TypeParameter)tp.get(MetamodelGenerator.KEY_METATYPE);
            if (tparm.getExtendedType() == null) {
                if (tp.containsKey(MetamodelGenerator.KEY_PACKAGE)) {
                    //Looks like this never happens but...
                    ProducedType subtype = getTypeFromJson(tp, container, allparms);
                    tparm.setExtendedType(subtype);
                } else if (tp.containsKey(MetamodelGenerator.KEY_TYPES)) {
                    if (!("u".equals(tp.get("comp")) || "i".equals(tp.get("comp")))) {
                        throw new IllegalArgumentException("Only union or intersection types are allowed as 'comp'");
                    }
                    ProducedType subtype = getTypeFromJson(tp, container, allparms);
                    tparm.setName(subtype.getProducedTypeName());
                    tparm.setExtendedType(subtype);
                } else {
                    tparm.setExtendedType(getTypeFromJson(voidclass, container, null));
                }
            }
            if (tparm.isDefaulted()) {
                @SuppressWarnings("unchecked")
                final Map<String,Object> deftype = (Map<String,Object>)tp.get(MetamodelGenerator.KEY_DEFAULT);
                tparm.setDefaultTypeArgument(getTypeFromJson(deftype, container, existing));
            }
            if (tp.containsKey(MetamodelGenerator.KEY_SATISFIES)) {
                @SuppressWarnings("unchecked")
                final List<Map<String,Object>> stypes = (List<Map<String,Object>>)tp.get(MetamodelGenerator.KEY_SATISFIES);
                for (ProducedType sat : parseTypeList(stypes, allparms)) {
                    Util.addToIntersection(tparm.getSatisfiedTypes(), sat, u2);
                }
                tparm.setConstrained(true);
            } else if (tp.containsKey("of")) {
                @SuppressWarnings("unchecked")
                final List<Map<String,Object>> oftype = (List<Map<String,Object>>)tp.get("of");
                tparm.setCaseTypes(parseTypeList(oftype, allparms));
                tparm.setConstrained(true);
            }
        }
        return tparms;
    }

    /** Creates a parameter list from a list of maps where each map represents a parameter.
     * @param The list of maps to create the parameters.
     * @param owner The declaration to assign to each parameter.
     * @param typeParameters The type parameters which can be referenced from the parameters. */
    private ParameterList parseParameters(List<Map<String,Object>> params, Declaration owner, List<TypeParameter> typeParameters) {
        ParameterList plist = new ParameterList();
        if (params != null) {
            for (Map<String,Object> p : params) {
                Parameter param = new Parameter();
                final String paramtype = (String)p.get("$pt");
                param.setHidden(p.containsKey("$hdn"));
                param.setName((String)p.get(MetamodelGenerator.KEY_NAME));
                param.setDeclaration(owner);
                param.setDefaulted(p.containsKey(MetamodelGenerator.KEY_DEFAULT));
                param.setSequenced(p.containsKey("seq"));
                param.setAtLeastOne(p.containsKey("$min1"));
                if (paramtype == null || "v".equals(paramtype)) {
                    Value _v = new Value();
                    param.setModel(_v);
                } else if ("f".equals(paramtype)) {
                    @SuppressWarnings("unchecked")
                    List<List<Map<String,Object>>> paramLists = (List<List<Map<String,Object>>>)p.get(MetamodelGenerator.KEY_PARAMS);
                    Method _m = new Method();
                    param.setModel(_m);
                    if (paramLists == null) {
                        _m.addParameterList(new ParameterList());
                    } else {
                        boolean first = true;
                        for (List<Map<String,Object>> subplist : paramLists) {
                            ParameterList _params = parseParameters(subplist, _m, typeParameters);
                            if (first) {
                                first = false;
                            } else {
                                _params.setNamedParametersSupported(false);
                            }
                            _m.addParameterList(_params);
                        }
                    }
                } else {
                    throw new IllegalArgumentException("Unknown parameter type " + paramtype);
                }
                if (param.getModel() != null) {
                    param.getModel().setInitializerParameter(param);
                    param.getModel().setName(param.getName());
                    param.getModel().setUnit(u2);
                    if (owner instanceof Scope) {
                        param.getModel().setContainer((Scope)owner);
                    }
                    if (p.get(MetamodelGenerator.KEY_TYPE) instanceof Map) {
                        @SuppressWarnings("unchecked")
                        final Map<String,Object> ktype = (Map<String,Object>)p.get(MetamodelGenerator.KEY_TYPE);
                        param.getModel().setType(getTypeFromJson(ktype, owner, typeParameters));
                    } else {
                        //parameter type
                        for (TypeParameter tp : typeParameters) {
                            if (tp.getName().equals(p.get(MetamodelGenerator.KEY_TYPE))) {
                                param.getModel().setType(tp.getType());
                            }
                        }
                    }
                    @SuppressWarnings("unchecked")
                    final Map<String,Object> _anns = (Map<String,Object>)p.remove(MetamodelGenerator.KEY_ANNOTATIONS);
                    setAnnotations(param.getModel(), (Integer)p.remove(MetamodelGenerator.KEY_PACKED_ANNS), _anns);
                }
                //owner.getMembers().add(param);
                plist.getParameters().add(param);
            }
        }
        return plist;
    }

    @SuppressWarnings("unchecked")
    Method loadMethod(String name, Map<String, Object> m, Scope parent, final List<TypeParameter> existing) {
        Method md = new Method();
        md.setName(name);
        m.remove(MetamodelGenerator.KEY_NAME);
        md.setContainer(parent);
        setAnnotations(md, (Integer)m.remove(MetamodelGenerator.KEY_PACKED_ANNS),
                (Map<String,Object>)m.remove(MetamodelGenerator.KEY_ANNOTATIONS));
        md.setUnit(u2);
        if (parent == this) {
            //Top-level declarations are directly added to the unit
            u2.addDeclaration(md);
            addMember(null);
        }
        final List<TypeParameter> tparms = parseTypeParameters(
                (List<Map<String,Object>>)m.get(MetamodelGenerator.KEY_TYPE_PARAMS), md, existing);
        final List<TypeParameter> allparms = JsonPackage.merge(tparms, existing);
        md.setType(getTypeFromJson((Map<String,Object>)m.remove(MetamodelGenerator.KEY_TYPE),
                parent instanceof Declaration ? (Declaration)parent : null, allparms));
        List<List<Map<String,Object>>> paramLists = (List<List<Map<String,Object>>>)m.remove(MetamodelGenerator.KEY_PARAMS);
        if (paramLists == null) {
            md.addParameterList(new ParameterList());
        } else {
            boolean first = true;
            for (List<Map<String,Object>> plist : paramLists) {
                ParameterList _params = parseParameters(plist, md, allparms);
                if (first) {
                    first = false;
                } else {
                    _params.setNamedParametersSupported(false);
                }
                md.addParameterList(_params);
            }
        }
        return md;
    }

    MethodOrValue loadAttribute(String name, Map<String, Object> m, Scope parent,
            List<TypeParameter> typeParameters) {
        String metatype = (String)m.get(MetamodelGenerator.KEY_METATYPE);
        Value d = new Value();
        d.setTransient(MetamodelGenerator.METATYPE_GETTER.equals(metatype));
        d.setName(name);
        d.setContainer(parent);
        d.setUnit(u2);
        if (parent == this) {
            u2.addDeclaration(d);
            addMember(null);
        }
        @SuppressWarnings("unchecked")
        final Map<String,Object> _anns = (Map<String,Object>)m.remove(MetamodelGenerator.KEY_ANNOTATIONS);
        setAnnotations(d, (Integer)m.remove(MetamodelGenerator.KEY_PACKED_ANNS), _anns);
        if (m.containsKey("var")) {
            ((Value)d).setVariable(true);
        }
        @SuppressWarnings("unchecked")
        final Map<String,Object> ktype = (Map<String,Object>)m.get(MetamodelGenerator.KEY_TYPE);
        d.setType(getTypeFromJson(ktype, parent instanceof Declaration ? (Declaration)parent : null, typeParameters));
        @SuppressWarnings("unchecked")
        final Map<String, Object> smap = (Map<String, Object>)m.remove("$set");
        if (smap != null) {
            Setter s = new Setter();
            s.setName(name);
            s.setContainer(parent);
            s.setUnit(u2);
            s.setGetter(d);
            d.setSetter(s);
            if (parent == this) {
                u2.addDeclaration(s);
                addMember(null);
            }
            @SuppressWarnings("unchecked")
            final Map<String,Object> sanns = (Map<String,Object>)smap.remove(MetamodelGenerator.KEY_ANNOTATIONS);
            setAnnotations(s, (Integer)smap.remove(MetamodelGenerator.KEY_PACKED_ANNS), sanns);
            s.setType(d.getType());
        }
        return d;
    }

    /** Sets the refined declarations for the type's members. */
    private void refineMembers(ClassOrInterface coi) {
        //fill refined declarations
        for (Declaration d : coi.getMembers()) {
            if (d.isActual()) {
                Declaration refined = coi.getRefinedMember(d.getName(), getSignature(d), false);
                if (refined==null) refined = d;
                d.setRefinedDeclaration(refined);
            }
            if (d instanceof ClassOrInterface) {
                refineMembers((ClassOrInterface)d);
            }
        }
    }

    @SuppressWarnings("unchecked")
    Interface loadInterface(String name, Map<String, Object> m, Scope parent, final List<TypeParameter> existing) {
        //Check if it's been loaded first
        //It hasn't been loaded, so create it
        Interface t;
        m.remove(MetamodelGenerator.KEY_NAME);
        if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof Interface) {
            t = (Interface)m.get(MetamodelGenerator.KEY_METATYPE);
            if (m.size() <= 3) {
                //it's been loaded
                return t;
            }
        } else {
            if (m.containsKey("$alias")) {
                t = new InterfaceAlias();
            } else {
                t = new Interface();
            }
            t.setContainer(parent);
            t.setName(name);
            t.setUnit(u2);
            if (parent == this) {
                u2.addDeclaration(t);
            }
            parent.addMember(t);
            m.put(MetamodelGenerator.KEY_METATYPE, t);
            setAnnotations(t, (Integer)m.remove(MetamodelGenerator.KEY_PACKED_ANNS),
                    (Map<String,Object>)m.remove(MetamodelGenerator.KEY_ANNOTATIONS));
        }
        if (m.remove(MetamodelGenerator.KEY_DYNAMIC) != null) {
            t.setDynamic(true);
        }
        List<TypeParameter> tparms = t.getTypeParameters();
        List<Map<String,Object>> listOfMaps = (List<Map<String,Object>>)m.get(MetamodelGenerator.KEY_TYPE_PARAMS);
        if (listOfMaps != null && (tparms == null || tparms.size() < listOfMaps.size())) {
            tparms = parseTypeParameters(listOfMaps, t, existing);
            m.remove(MetamodelGenerator.KEY_TYPE_PARAMS);
        }
        final List<TypeParameter> allparms = JsonPackage.merge(tparms, existing);
        //All interfaces extend Object, except aliases
        if (t.getExtendedType() == null) {
            if (t.isAlias()) {
                t.setExtendedType(getTypeFromJson((Map<String,Object>)m.remove("$alias"),
                        parent instanceof Declaration ? (Declaration)parent : null, allparms));
            } else {
                t.setExtendedType(getTypeFromJson(objclass,
                        parent instanceof Declaration ? (Declaration)parent : null, null));
            }
        }
        if (m.containsKey(MetamodelGenerator.KEY_SELF_TYPE)) {
            for (TypeParameter _tp : tparms) {
                if (_tp.getName().equals(m.get(MetamodelGenerator.KEY_SELF_TYPE))) {
                    t.setSelfType(_tp.getType());
                    _tp.setSelfTypedDeclaration(t);
                }
            }
            m.remove(MetamodelGenerator.KEY_SELF_TYPE);
        }
        if (m.containsKey("of") && t.getCaseTypes() == null) {
            t.setCaseTypes(parseTypeList((List<Map<String,Object>>)m.remove("of"), allparms));
        }
        if (m.containsKey(MetamodelGenerator.KEY_SATISFIES)) {
            for (ProducedType s : parseTypeList((List<Map<String,Object>>)m.remove(MetamodelGenerator.KEY_SATISFIES), allparms)) {
                Util.addToIntersection(t.getSatisfiedTypes(), s, u2);
            }
        }
        addAttributesAndMethods(m, t, allparms);
        return t;
    }

    /** Loads an object declaration, creating it if necessary, and returns its type declaration. */
    @SuppressWarnings("unchecked")
    TypeDeclaration loadObject(String name, Map<String, Object> m, Scope parent, List<TypeParameter> existing) {
        Value obj;
        if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof Value) {
            obj = (Value)m.get(MetamodelGenerator.KEY_METATYPE);
        } else {
            obj = new Value();
            m.put(MetamodelGenerator.KEY_METATYPE, obj);
            obj.setName(name);
            obj.setContainer(parent);
            obj.setUnit(u2);
            com.redhat.ceylon.compiler.typechecker.model.Class type = new com.redhat.ceylon.compiler.typechecker.model.Class();
            type.setName(name);
            type.setAnonymous(true);
            type.setUnit(u2);
            type.setContainer(parent);
            if (parent == this) {
                u2.addDeclaration(obj);
                u2.addDeclaration(type);
            }
            parent.addMember(obj);
            obj.setType(type.getType());
            setAnnotations(obj, (Integer)m.get(MetamodelGenerator.KEY_PACKED_ANNS),
                    (Map<String,Object>)m.get(MetamodelGenerator.KEY_ANNOTATIONS));
            setAnnotations(obj.getTypeDeclaration(), (Integer)m.remove(MetamodelGenerator.KEY_PACKED_ANNS),
                    (Map<String,Object>)m.remove(MetamodelGenerator.KEY_ANNOTATIONS));
            if (type.getExtendedType() == null) {
                if (m.containsKey("super")) {
                    type.setExtendedType(getTypeFromJson((Map<String,Object>)m.remove("super"),
                            parent instanceof Declaration ? (Declaration)parent : null, existing));
                } else {
                    type.setExtendedType(getTypeFromJson(idobj, parent instanceof Declaration ? (Declaration)parent : null, existing));
                }
            }
            if (m.containsKey(MetamodelGenerator.KEY_SATISFIES)) {
                for (ProducedType sat : parseTypeList((List<Map<String,Object>>)m.remove(MetamodelGenerator.KEY_SATISFIES), existing)) {
                    Util.addToIntersection(type.getSatisfiedTypes(), sat, u2);
                }
            }
            if (m.containsKey(MetamodelGenerator.KEY_INTERFACES)) {
                for (Map.Entry<String,Map<String,Object>> inner : ((Map<String,Map<String,Object>>)m.remove(MetamodelGenerator.KEY_INTERFACES)).entrySet()) {
                    loadInterface(inner.getKey(), inner.getValue(), type, existing);
                }
            }
            if (m.containsKey(MetamodelGenerator.KEY_CLASSES)) {
                for (Map.Entry<String,Map<String,Object>> inner : ((Map<String,Map<String,Object>>)m.remove(MetamodelGenerator.KEY_CLASSES)).entrySet()) {
                    loadClass(inner.getKey(), inner.getValue(), type, existing);
                }
            }
            if (m.containsKey(MetamodelGenerator.KEY_OBJECTS)) {
                for (Map.Entry<String,Map<String,Object>> inner : ((Map<String,Map<String,Object>>)m.remove(MetamodelGenerator.KEY_OBJECTS)).entrySet()) {
                    loadObject(inner.getKey(), inner.getValue(), type, existing);
                }
            }
            addAttributesAndMethods(m, type, existing);
        }
        return obj.getTypeDeclaration();
    }

    /** Load a type alias, creating it if necessary.
     * @return The TypeAlias declaration. */
    @SuppressWarnings("unchecked")
    private TypeAlias loadTypeAlias(String name, Map<String, Object> m, Scope parent, List<TypeParameter> existing) {
        TypeAlias alias;
        if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof TypeAlias) {
            //It's been loaded already
            alias = (TypeAlias)m.get(MetamodelGenerator.KEY_METATYPE);
            if (m.size() == 1) {
                return alias;
            }
        } else {
            Declaration maybe = parent.getDirectMember(name, null, false);
            if (maybe == null) {
                alias = new TypeAlias();
                alias.setContainer(parent);
                alias.setName(name);
                alias.setUnit(u2);
                setAnnotations(alias, (Integer)m.remove(MetamodelGenerator.KEY_PACKED_ANNS),
                        (Map<String,Object>)m.remove(MetamodelGenerator.KEY_ANNOTATIONS));
                if (parent == this) {
                    u2.addDeclaration(alias);
                }
                parent.addMember(alias);
                m.put(MetamodelGenerator.KEY_METATYPE, alias);
            } else if (maybe instanceof TypeAlias) {
                alias = (TypeAlias)maybe;
            } else {
                throw new IllegalStateException(maybe + " should be an TypeAlias");
            }
        }
        //All interfaces extend Object, except aliases
        if (alias.getExtendedType() == null) {
            alias.setExtendedType(getTypeFromJson((Map<String,Object>)m.get("$alias"),
                    parent instanceof Declaration ? (Declaration)parent : null, existing));
        }
        List<Map<String,Object>> listOfMaps = (List<Map<String,Object>>)m.get(MetamodelGenerator.KEY_TYPE_PARAMS);
        final List<TypeParameter> tparms;
        if (listOfMaps != null && alias.getTypeParameters().size()<listOfMaps.size()) {
            tparms = parseTypeParameters(listOfMaps, alias, existing);
        } else {
            tparms = alias.getTypeParameters();
        }
        final List<TypeParameter> allparms = JsonPackage.merge(tparms, existing);
        if (m.containsKey(MetamodelGenerator.KEY_SELF_TYPE)) {
            for (TypeParameter _tp : tparms) {
                if (_tp.getName().equals(m.get(MetamodelGenerator.KEY_SELF_TYPE))) {
                    alias.setSelfType(_tp.getType());
                }
            }
        }
        if (m.containsKey("of")) {
            alias.setCaseTypes(parseTypeList((List<Map<String,Object>>)m.remove("of"), allparms));
        }
        if (m.containsKey(MetamodelGenerator.KEY_SATISFIES)) {
            for (ProducedType sat :parseTypeList((List<Map<String,Object>>)m.remove(MetamodelGenerator.KEY_SATISFIES), allparms)) {
                Util.addToIntersection(alias.getSatisfiedTypes(), sat, u2);
            }
        }
        m.clear();
        m.put(MetamodelGenerator.KEY_METATYPE, alias);
        return alias;
    }

    /** Looks up a type from model data, creating it if necessary. The returned type will have its
     * type parameters substituted if needed. */
    private ProducedType getTypeFromJson(Map<String, Object> m, Declaration container, List<TypeParameter> typeParams) {
        TypeDeclaration td = null;
        if (m.get(MetamodelGenerator.KEY_METATYPE) instanceof TypeDeclaration) {
            td = (TypeDeclaration)m.get(MetamodelGenerator.KEY_METATYPE);
            if (td instanceof ClassOrInterface && td.getUnit().getPackage() instanceof JsonPackage) {
                ((JsonPackage)td.getUnit().getPackage()).load(td.getName(), typeParams);
            }
        }
        final String tname = (String)m.get(MetamodelGenerator.KEY_NAME);
        if ("$U".equals(tname)) {
            m.put(MetamodelGenerator.KEY_METATYPE, unknown);
            return unknown.getType();
        }
        if (td == null && m.containsKey("comp")) {
            @SuppressWarnings("unchecked")
            final List<Map<String,Object>> tmaps = (List<Map<String,Object>>)m.get(MetamodelGenerator.KEY_TYPES);
            final ArrayList<ProducedType> types = new ArrayList<ProducedType>(tmaps.size());
            if ("u".equals(m.get("comp"))) {
                UnionType ut = new UnionType(u2);
                for (Map<String, Object> tmap : tmaps) {
                    Util.addToUnion(types, getTypeFromJson(tmap, container, typeParams));
                }
                ut.setCaseTypes(types);
                td = ut;
            } else if ("i".equals(m.get("comp"))) {
                IntersectionType it = new IntersectionType(u2);
                for (Map<String, Object> tmap : tmaps) {
                    Util.addToIntersection(types, getTypeFromJson(tmap, container, typeParams), u2);
                }
                it.setSatisfiedTypes(types);
                td = it;
            } else {
                throw new IllegalArgumentException("Invalid composite type '" + m.get("comp") + "'");
            }
        } else if (td == null) {
            final String pname = (String)m.get(MetamodelGenerator.KEY_PACKAGE);
            if (pname == null) {
                //It's a ref to a type parameter
                final List<TypeParameter> containerTypeParameters = container instanceof Generic ?
                    ((Generic)container).getTypeParameters() : null;
                if (containerTypeParameters != null) {
                    for (TypeParameter typeParam : containerTypeParameters) {
                        if (typeParam.getName().equals(tname)) {
                            td = typeParam;
                        }
                    }
                }
                if (td == null) {
                    for (TypeParameter typeParam : typeParams) {
                        if (typeParam.getName().equals(tname)) {
                            td = typeParam;
                        }
                    }
                }
            } else {
                String mname = (String)m.get(MetamodelGenerator.KEY_MODULE);
                if ("$".equals(mname)) {
                    mname = Module.LANGUAGE_MODULE_NAME;
                }
                com.redhat.ceylon.compiler.typechecker.model.Package rp;
                if ("$".equals(pname)) {
                    //Language module package
                    rp = Module.LANGUAGE_MODULE_NAME.equals(getNameAsString())? this :
                        getModule().getLanguageModule().getDirectPackage(Module.LANGUAGE_MODULE_NAME);
                } else if (mname == null) {
                    //local type
                    if (".".equals(pname)) {
                        rp = this;
                        if (container instanceof TypeDeclaration && tname.equals(container.getName())) {
                            td = (TypeDeclaration)container;
                        }
                    } else {
                        rp = getModule().getDirectPackage(pname);
                    }
                } else {
                    rp = getModule().getPackage(pname);
                }
                if (rp == null) {
                    throw new CompilerErrorException("Package not found: " + pname);
                }
                if (rp != this && rp instanceof JsonPackage && !((JsonPackage)rp).loaded) {
                    ((JsonPackage) rp).loadDeclarations();
                }
                final boolean nested = tname.indexOf('.') > 0;
                final String level1 = nested ? tname.substring(0, tname.indexOf('.')) : tname;
                if (rp != null && !nested) {
                    Declaration d = rp.getDirectMember(tname, null, false);
                    if (d instanceof TypeDeclaration) {
                        td = (TypeDeclaration)d;
                    } else if (d instanceof MethodOrValue) {
                        td = ((MethodOrValue)d).getTypeDeclaration();
                    }
                }
                if (td == null && rp instanceof JsonPackage) {
                    if (nested) {
                        td = ((JsonPackage)rp).loadNestedType(tname, typeParams);
                    } else {
                        td = (TypeDeclaration)((JsonPackage)rp).load(tname, typeParams);
                    }
                }
                //Then look in the top-level declarations
                if (nested && td == null) {
                    for (Declaration d : rp.getMembers()) {
                        if (d instanceof TypeDeclaration && level1.equals(d.getName())) {
                            td = (TypeDeclaration)d;
                        }
                    }
                    final String[] path = tname.split("\\.");
                    for (int i = 1; i < path.length; i++) {
                        td = (TypeDeclaration)td.getDirectMember(path[i], null, false);
                    }
                }
            }
        }
        @SuppressWarnings("unchecked")
        final List<Map<String,Object>> modelParms = (List<Map<String,Object>>)m.get(MetamodelGenerator.KEY_TYPE_PARAMS);
        if (td != null && modelParms != null) {
            //Substitute type parameters
            final HashMap<TypeParameter, ProducedType> concretes = new HashMap<TypeParameter, ProducedType>();
            HashMap<TypeParameter,SiteVariance> variances = null;
            if (td.getTypeParameters().size() < modelParms.size()) {
                if (td.getUnit().getPackage() == this) {
                    parseTypeParameters(modelParms, td, null);
                }
            }
            final Iterator<TypeParameter> viter = td.getTypeParameters().iterator();
            for (Map<String,Object> ptparm : modelParms) {
                TypeParameter _cparm = viter.next();
                if (ptparm.containsKey(MetamodelGenerator.KEY_PACKAGE) || ptparm.containsKey(MetamodelGenerator.KEY_TYPES)) {
                    //Substitute for proper type
                    final ProducedType _pt = getTypeFromJson(ptparm, container, typeParams);
                    if (ptparm.containsKey(MetamodelGenerator.KEY_US_VARIANCE)) {
                        if (variances == null) {
                            variances = new HashMap<>();
                        }
                        variances.put(_cparm, SiteVariance.values()[(int)ptparm.get(MetamodelGenerator.KEY_US_VARIANCE)]);
                    }
                    concretes.put(_cparm, _pt);
                } else if (ptparm.containsKey(MetamodelGenerator.KEY_NAME)) {
                    //Look for type parameter with same name
                    for (TypeParameter typeParam : typeParams) {
                        if (typeParam.getName().equals(ptparm.get(MetamodelGenerator.KEY_NAME))) {
                            concretes.put(_cparm, typeParam.getType());
                        }
                    }
                }
            }
            if (!concretes.isEmpty()) {
                ProducedType rval = td.getType().substitute(concretes);
                if (variances != null) {
                    rval.setVarianceOverrides(variances);
                }
                return rval;
            }
        }
        if (td == null) {
            try {
                throw new IllegalArgumentException(String.format("Couldn't find type %s::%s for %s in %s<%s> (FROM pkg %s)",
                        m.get(MetamodelGenerator.KEY_PACKAGE), m.get(MetamodelGenerator.KEY_NAME),
                        m.get(MetamodelGenerator.KEY_MODULE), m, typeParams, getNameAsString()));
            } catch (IllegalArgumentException ex) {
                ex.printStackTrace();
            }
        }
        return td.getType();
    }

    /** Load a top-level declaration with the specified name, by parsing its model data. */
    Declaration load(String name, List<TypeParameter> existing) {
        if (model == null) {
            throw new IllegalStateException("No model available to load " + name);
        }
        @SuppressWarnings("unchecked")
        final Map<String,Object> map = model == null ? null : (Map<String,Object>)model.get(name);
        if (map == null) {
            if ("Nothing".equals(name)) {
                //Load Nothing from language module, regardless of what this package is
                return nothing;
            } else if ("$U".equals(name)) {
                return unknown;
            }
            throw new IllegalStateException("Cannot find " + pkgname + "::" + name + " in " + model.keySet());
        }
        Object metatype = map.get(MetamodelGenerator.KEY_METATYPE);
        if (metatype == null) {
            throw new IllegalArgumentException("Missing metatype from entry " + map);
        }
        if (metatype.equals(MetamodelGenerator.METATYPE_ATTRIBUTE)
                || metatype.equals(MetamodelGenerator.METATYPE_GETTER)) {
            return loadAttribute(name, map, this, null);
        } else if (metatype.equals(MetamodelGenerator.METATYPE_CLASS) || metatype instanceof com.redhat.ceylon.compiler.typechecker.model.Class) {
            return loadClass(name, map, this, existing);
        } else if (metatype.equals(MetamodelGenerator.METATYPE_INTERFACE) || metatype instanceof com.redhat.ceylon.compiler.typechecker.model.Interface) {
            return loadInterface(name, map, this, existing);
        } else if (metatype.equals(MetamodelGenerator.METATYPE_METHOD)) {
            return loadMethod(name, map, this, existing);
        } else if (metatype.equals(MetamodelGenerator.METATYPE_OBJECT) || metatype instanceof Value) {
            return loadObject(name, map, this, existing);
        } else if (metatype.equals(MetamodelGenerator.METATYPE_ALIAS)) {
            return loadTypeAlias(name, map, this, existing);
        }
        System.out.println("WTF is this shit " + map);
        return null;
    }

    public static boolean hasAnnotationBit(int bits, String annotationName) {
        final int idx = MetamodelGenerator.annotationBits.indexOf(annotationName);
        if (idx < 0) return false;
        return (bits & (1 << idx)) > 0;
    }

    private void setAnnotations(Declaration d, Integer bits, Map<String,Object> m) {
        if (bits != null) {
            d.setShared(hasAnnotationBit(bits, "shared"));
            d.setActual(hasAnnotationBit(bits, "actual"));
            d.setFormal(hasAnnotationBit(bits, "formal"));
            d.setDefault(hasAnnotationBit(bits, "default"));
            d.setNative(hasAnnotationBit(bits, "native"));
            d.setAnnotation(hasAnnotationBit(bits, "annotation"));
            if (hasAnnotationBit(bits, "sealed")) {
                ((TypeDeclaration)d).setSealed(true);
            }
            if (d instanceof com.redhat.ceylon.compiler.typechecker.model.Class) {
                ((com.redhat.ceylon.compiler.typechecker.model.Class)d).setFinal(hasAnnotationBit(bits, "final"));
                ((com.redhat.ceylon.compiler.typechecker.model.Class)d).setAbstract(hasAnnotationBit(bits, "abstract"));
            }
            if (hasAnnotationBit(bits, "late")) {
                ((Value)d).setLate(true);
            }
            if (hasAnnotationBit(bits, "variable")) {
                ((Value)d).setVariable(true);
            }
        }
        if (m == null) return;
        for (Map.Entry<String, Object> e : m.entrySet()) {
            String name = e.getKey();
            Annotation ann = new Annotation();
            ann.setName(name);
            if ("doc".equals(name) && e.getValue() instanceof Integer) {
                ann.addPositionalArgment(docs.get((Integer)e.getValue()));
            } else {
                for (String arg : (List<String>)e.getValue()) {
                    ann.addPositionalArgment(arg);
                }
            }
            d.getAnnotations().add(ann);
        }
        //This is to avoid problems with private declarations while
        //compiling the language module
        if (JsCompiler.isCompilingLanguageModule() && d.isToplevel() && !d.isShared()) {
            d.setShared(true);
        }
    }

    /** Load a nested type that hasn't been loaded yet */
    private TypeDeclaration loadNestedType(final String fqn, List<TypeParameter> typeParams) {
        String[] path = fqn.split("\\.");
        @SuppressWarnings("unchecked")
        Map<String,Object> typeMap = (Map<String,Object>)model.get(path[0]);
        if (typeMap.get(MetamodelGenerator.KEY_METATYPE) instanceof TypeDeclaration == false) {
            load(path[0], typeParams);
        }
        TypeDeclaration td = (TypeDeclaration)typeMap.get(MetamodelGenerator.KEY_METATYPE);
        for (int i = 1; i < path.length; i++) {
            @SuppressWarnings("unchecked")
            Map<String,Object> subtypes = (Map<String,Object>)typeMap.get(MetamodelGenerator.KEY_INTERFACES);
            Map<String,Object> childMap = null;
            int type = 0;
            if (subtypes != null) {
                childMap = (Map<String,Object>)subtypes.get(path[i]);
                type = 1;
            }
            if (childMap == null) {
                subtypes = (Map<String,Object>)typeMap.get(MetamodelGenerator.KEY_CLASSES);
                if (subtypes != null) {
                    childMap = (Map<String,Object>)subtypes.get(path[i]);
                    type = 2;
                }
            }
            TypeDeclaration child = (TypeDeclaration)td.getDirectMember(path[i], null, false);
            if (child == null) {
                switch(type) {
                case 1:child = loadInterface(path[i], childMap, td, typeParams);
                break;
                case 2:child = loadClass(path[i], childMap, td, typeParams);
                break;
                }
            }
            td = child;
        }
        return td;
    }

    /** Create a new list that contains all the type parameters in the first list,
     * and the type parameters from the second list that don't have any names already present in the first list.
     */
    public static List<TypeParameter> merge(List<TypeParameter> l1, List<TypeParameter> l2) {
        int size = (l1 == null ? 0 : l1.size()) + (l2 == null ? 0 : l2.size());
        ArrayList<TypeParameter> merged = new ArrayList<TypeParameter>(size);
        HashSet<String> names = new HashSet<String>();
        if (l1 != null) {
            for (TypeParameter t : l1) {
                merged.add(t);
                names.add(t.getName());
            }
        }
        if (l2 != null) {
            for (TypeParameter t : l2) {
                if (!names.contains(t.getName())) {
                    merged.add(t);
                }
            }
        }
        return merged;
    }

}
TOP

Related Classes of com.redhat.ceylon.compiler.loader.JsonPackage

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.