com.redhat.ceylon.eclipse.core.model.loader.JDTModelLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.ceylon.eclipse.core.model.loader.JDTModelLoader.java

Source

/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */

package com.redhat.ceylon.eclipse.core.model.loader;

import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.isInCeylonClassesOutputFolder;

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 java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.ISourceType;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;

import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.compiler.java.loader.TypeFactory;
import com.redhat.ceylon.compiler.java.util.Timer;
import com.redhat.ceylon.compiler.java.util.Util;
import com.redhat.ceylon.compiler.loader.AbstractModelLoader;
import com.redhat.ceylon.compiler.loader.SourceDeclarationVisitor;
import com.redhat.ceylon.compiler.loader.TypeParser;
import com.redhat.ceylon.compiler.loader.mirror.ClassMirror;
import com.redhat.ceylon.compiler.loader.mirror.MethodMirror;
import com.redhat.ceylon.compiler.loader.model.LazyClass;
import com.redhat.ceylon.compiler.loader.model.LazyInterface;
import com.redhat.ceylon.compiler.loader.model.LazyMethod;
import com.redhat.ceylon.compiler.loader.model.LazyPackage;
import com.redhat.ceylon.compiler.loader.model.LazyValue;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleManager;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.ExternalUnit;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.Modules;
import com.redhat.ceylon.compiler.typechecker.model.Package;
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.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.Unit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder;
import com.redhat.ceylon.eclipse.core.model.CeylonDeclaration;

/**
 * A model loader which uses the JDT model.
 *
 * @author David Festal <david.festal@serli.com>
 */
public class JDTModelLoader extends AbstractModelLoader {

    private IJavaProject javaProject;
    private CompilerOptions compilerOptions;

    private ProblemReporter problemReporter;
    private LookupEnvironment lookupEnvironment;
    private boolean mustResetLookupEnvironment = false;

    private Map<String, Declaration> languageModuledeclarations;

    public JDTModelLoader(final ModuleManager moduleManager, final Modules modules) {
        this.moduleManager = moduleManager;
        this.modules = modules;
        javaProject = ((JDTModuleManager) moduleManager).getJavaProject();
        compilerOptions = new CompilerOptions(javaProject.getOptions(true));
        compilerOptions.ignoreMethodBodies = true;
        compilerOptions.storeAnnotations = true;
        problemReporter = new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(),
                compilerOptions, new DefaultProblemFactory());
        this.timer = new Timer(false);
        internalCreate();
    }

    private void internalCreate() {
        this.languageModuledeclarations = new HashMap<String, Declaration>();
        this.typeFactory = new TypeFactory(moduleManager.getContext()) {
            @Override
            public Package getPackage() {
                synchronized (JDTModelLoader.this) {
                    if (super.getPackage() == null) {
                        super.setPackage(modules.getLanguageModule().getDirectPackage("ceylon.language"));
                    }
                    return super.getPackage();
                }
            }

            /**
             * Search for a declaration in the language module. 
             */
            public Declaration getLanguageModuleDeclaration(String name) {
                synchronized (JDTModelLoader.this) {
                    if (languageModuledeclarations.containsKey(name)) {
                        return languageModuledeclarations.get(name);
                    }

                    languageModuledeclarations.put(name, null);
                    Declaration decl = super.getLanguageModuleDeclaration(name);
                    languageModuledeclarations.put(name, decl);
                    return decl;
                }
            }
        };
        this.typeParser = new TypeParser(this, typeFactory);
        this.timer = new Timer(false);
        createLookupEnvironment();
    }

    public void createLookupEnvironment() {
        try {
            lookupEnvironment = new LookupEnvironment(new ITypeRequestor() {

                private Parser basicParser;

                @Override
                public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding,
                        AccessRestriction accessRestriction) {
                    // case of SearchableEnvironment of an IJavaProject is used
                    ISourceType sourceType = sourceTypes[0];
                    while (sourceType.getEnclosingType() != null)
                        sourceType = sourceType.getEnclosingType();
                    if (sourceType instanceof SourceTypeElementInfo) {
                        // get source
                        SourceTypeElementInfo elementInfo = (SourceTypeElementInfo) sourceType;
                        IType type = elementInfo.getHandle();
                        ICompilationUnit sourceUnit = (ICompilationUnit) type.getCompilationUnit();
                        accept(sourceUnit, accessRestriction);
                    } else {
                        CompilationResult result = new CompilationResult(sourceType.getFileName(), 1, 1, 0);
                        CompilationUnitDeclaration unit = SourceTypeConverter.buildCompilationUnit(sourceTypes,
                                SourceTypeConverter.FIELD_AND_METHOD // need field and methods
                                        | SourceTypeConverter.MEMBER_TYPE, // need member types
                                // no need for field initialization
                                lookupEnvironment.problemReporter, result);
                        lookupEnvironment.buildTypeBindings(unit, accessRestriction);
                        lookupEnvironment.completeTypeBindings(unit, true);
                    }
                }

                @Override
                public void accept(IBinaryType binaryType, PackageBinding packageBinding,
                        AccessRestriction accessRestriction) {
                    lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction);
                }

                @Override
                public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) {
                    // Switch the current policy and compilation result for this unit to the requested one.
                    CompilationResult unitResult = new CompilationResult(sourceUnit, 1, 1,
                            compilerOptions.maxProblemsPerUnit);
                    try {
                        CompilationUnitDeclaration parsedUnit = basicParser().dietParse(sourceUnit, unitResult);
                        lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction);
                        lookupEnvironment.completeTypeBindings(parsedUnit, true);
                    } catch (AbortCompilationUnit e) {
                        // at this point, currentCompilationUnitResult may not be sourceUnit, but some other
                        // one requested further along to resolve sourceUnit.
                        if (unitResult.compilationUnit == sourceUnit) { // only report once
                            //requestor.acceptResult(unitResult.tagAsAccepted());
                        } else {
                            throw e; // want to abort enclosing request to compile
                        }
                    }
                    // Display unit error in debug mode
                    if (BasicSearchEngine.VERBOSE) {
                        if (unitResult.problemCount > 0) {
                            System.out.println(unitResult);
                        }
                    }
                }

                private Parser basicParser() {
                    if (this.basicParser == null) {
                        ProblemReporter problemReporter = new ProblemReporter(
                                DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions,
                                new DefaultProblemFactory());
                        this.basicParser = new Parser(problemReporter, false);
                        this.basicParser.reportOnlyOneSyntaxError = true;
                    }
                    return this.basicParser;
                }
            }, compilerOptions, problemReporter,
                    ((JavaProject) javaProject).newSearchableNameEnvironment((WorkingCopyOwner) null));
        } catch (JavaModelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    // TODO : remove when the bug in the AbstractModelLoader is corrected
    @Override
    public synchronized LazyPackage findOrCreatePackage(Module module, String pkgName) {
        LazyPackage pkg = super.findOrCreatePackage(module, pkgName);
        if ("".equals(pkgName)) {
            pkg.setName(Collections.<String>emptyList());
        }
        if (pkg.getModule() != null && pkg.getModule().isJava()) {
            pkg.setShared(true);
        }
        Module currentModule = pkg.getModule();
        if (currentModule.equals(modules.getDefaultModule()) && !currentModule.equals(module)) {
            currentModule.getPackages().remove(pkg);
            pkg.setModule(null);
            if (module != null) {
                module.getPackages().add(pkg);
                pkg.setModule(module);
            }
        }
        return pkg;
    }

    @Override
    public void loadStandardModules() {
        // do not load the jdk modules unless imported explicitely

        /*
         * We start by loading java.lang because we will need it no matter what.
         */
        loadPackage("java.lang", false);
    }

    private String getQualifiedName(final String pkgName, String name) {
        // FIXME: some refactoring needed
        name = Util.quoteIfJavaKeyword(name);
        String className = pkgName.isEmpty() ? name : Util.quoteJavaKeywords(pkgName) + "." + name;
        return className;
    }

    @Override
    public synchronized boolean loadPackage(String packageName, boolean loadDeclarations) {
        packageName = Util.quoteJavaKeywords(packageName);
        if (loadDeclarations && !loadedPackages.add(packageName)) {
            return true;
        }
        Module module = lookupModuleInternal(packageName);

        if (module instanceof JDTModule) {
            JDTModule jdtModule = (JDTModule) module;
            List<IPackageFragmentRoot> roots = jdtModule.getPackageFragmentRoots();
            IPackageFragment packageFragment = null;
            for (IPackageFragmentRoot root : roots) {
                // skip packages that are not present
                if (!root.exists())
                    continue;
                try {
                    IClasspathEntry entry = root.getRawClasspathEntry();

                    //TODO: is the following really necessary?
                    //Note that getContentKind() returns an undefined
                    //value for a classpath container or variable
                    if (entry.getEntryKind() != IClasspathEntry.CPE_CONTAINER
                            && entry.getEntryKind() != IClasspathEntry.CPE_VARIABLE
                            && entry.getContentKind() == IPackageFragmentRoot.K_SOURCE
                            && !CeylonBuilder.isCeylonSourceEntry(entry)) {
                        continue;
                    }

                    packageFragment = root.getPackageFragment(packageName);
                    if (packageFragment.exists()) {
                        if (!loadDeclarations) {
                            // we found the package
                            return true;
                        } else {
                            try {
                                for (IClassFile classFile : packageFragment.getClassFiles()) {
                                    // skip removed class files
                                    if (!classFile.exists())
                                        continue;
                                    IType type = classFile.getType();
                                    if (type.exists() && !type.isMember()
                                            && !sourceDeclarations.containsKey(
                                                    getQualifiedName(type.getPackageFragment().getElementName(),
                                                            type.getTypeQualifiedName()))) {
                                        convertToDeclaration(type.getFullyQualifiedName(), DeclarationType.VALUE);
                                    }
                                }
                                for (org.eclipse.jdt.core.ICompilationUnit compilationUnit : packageFragment
                                        .getCompilationUnits()) {
                                    // skip removed CUs
                                    if (!compilationUnit.exists())
                                        continue;
                                    for (IType type : compilationUnit.getTypes()) {
                                        if (type.exists() && !type.isMember()
                                                && !sourceDeclarations.containsKey(
                                                        getQualifiedName(type.getPackageFragment().getElementName(),
                                                                type.getTypeQualifiedName()))) {
                                            convertToDeclaration(type.getFullyQualifiedName(),
                                                    DeclarationType.VALUE);
                                        }
                                    }
                                }
                            } catch (JavaModelException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (JavaModelException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    synchronized private LookupEnvironment getLookupEnvironment() {
        if (mustResetLookupEnvironment) {
            lookupEnvironment.reset();
            mustResetLookupEnvironment = false;
        }
        return lookupEnvironment;
    }

    @Override
    public synchronized ClassMirror lookupNewClassMirror(String name) {
        if (sourceDeclarations.containsKey(name)) {
            return new SourceClass(sourceDeclarations.get(name));
        }

        ClassMirror mirror = buildClassMirror(name);
        if (mirror == null && lastPartHasLowerInitial(name)) {
            // We have to try the unmunged name first, so that we find the symbol
            // from the source in preference to the symbol from any 
            // pre-existing .class file
            mirror = buildClassMirror(name + "_");
        }
        return mirror;
    }

    private ClassMirror buildClassMirror(String name) {
        try {
            IType type = javaProject.findType(name);
            if (type == null) {
                return null;
            }

            LookupEnvironment theLookupEnvironment = getLookupEnvironment();
            if (type.isBinary()) {
                ClassFile classFile = (ClassFile) type.getClassFile();

                if (classFile != null) {
                    IPackageFragmentRoot fragmentRoot = classFile.getPackageFragmentRoot();
                    if (fragmentRoot != null) {
                        if (isInCeylonClassesOutputFolder(fragmentRoot.getPath())) {
                            return null;
                        }
                    }

                    IFile classFileRsrc = (IFile) classFile.getCorrespondingResource();
                    IBinaryType binaryType = classFile.getBinaryTypeInfo(classFileRsrc, true);
                    if (classFileRsrc != null && !classFileRsrc.exists()) {
                        //the .class file has been deleted
                        return null;
                    }
                    BinaryTypeBinding binaryTypeBinding = theLookupEnvironment.cacheBinaryType(binaryType, null);
                    if (binaryTypeBinding == null) {
                        char[][] compoundName = CharOperation.splitOn('/', binaryType.getName());
                        ReferenceBinding existingType = theLookupEnvironment.getCachedType(compoundName);
                        if (existingType == null || !(existingType instanceof BinaryTypeBinding)) {
                            return null;
                        }
                        binaryTypeBinding = (BinaryTypeBinding) existingType;
                    }
                    return new JDTClass(binaryTypeBinding, theLookupEnvironment);
                }
            } else {
                char[][] compoundName = CharOperation.splitOn('.', type.getFullyQualifiedName().toCharArray());
                ReferenceBinding referenceBinding = theLookupEnvironment.getType(compoundName);
                if (referenceBinding != null) {
                    if (referenceBinding instanceof ProblemReferenceBinding) {
                        ProblemReferenceBinding problemReferenceBinding = (ProblemReferenceBinding) referenceBinding;
                        if (problemReferenceBinding.problemId() == ProblemReasons.InternalNameProvided) {
                            referenceBinding = problemReferenceBinding.closestReferenceMatch();
                        } else {
                            System.out.println(ProblemReferenceBinding
                                    .problemReasonString(problemReferenceBinding.problemId()));
                            return null;
                        }
                    }
                    return new JDTClass(referenceBinding, theLookupEnvironment);
                }
            }
        } catch (JavaModelException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public synchronized Declaration convertToDeclaration(String typeName, DeclarationType declarationType) {
        if (sourceDeclarations.containsKey(typeName)) {
            return sourceDeclarations.get(typeName).getModelDeclaration();
        }
        try {
            return super.convertToDeclaration(typeName, declarationType);
        } catch (RuntimeException e) {
            return null;
        }
    }

    @Override
    public void addModuleToClassPath(Module module, ArtifactResult artifact) {
    }

    @Override
    protected boolean isOverridingMethod(MethodMirror methodSymbol) {
        return ((JDTMethod) methodSymbol).isOverridingMethod();
    }

    @Override
    protected Unit getCompiledUnit(LazyPackage pkg, ClassMirror classMirror) {
        Unit unit = null;
        JDTClass jdtClass = (JDTClass) classMirror;
        String unitName = jdtClass.getFileName();
        if (!jdtClass.isBinary()) {
            for (Unit unitToTest : pkg.getUnits()) {
                if (unitToTest.getFilename().equals(unitName)) {
                    return unitToTest;
                }
            }
        }
        unit = new ExternalUnit();
        unit.setFilename(jdtClass.getFileName());
        unit.setPackage(pkg);
        return unit;
    }

    @Override
    protected void logError(String message) {
        //System.err.println("ERROR: "+message);
    }

    @Override
    protected void logWarning(String message) {
        //System.err.println("WARNING: "+message);
    }

    @Override
    protected void logVerbose(String message) {
        //System.err.println("NOTE: "+message);
    }

    @Override
    public synchronized void removeDeclarations(List<Declaration> declarations) {
        List<Declaration> allDeclarations = new ArrayList<Declaration>(declarations.size());
        allDeclarations.addAll(declarations);

        for (Declaration declaration : declarations) {
            retrieveInnerDeclarations(declaration, allDeclarations);
        }

        for (Declaration decl : allDeclarations) {
            String fqn = getQualifiedName(decl.getContainer().getQualifiedNameString(), decl.getName());
            sourceDeclarations.remove(fqn);
        }

        super.removeDeclarations(allDeclarations);
        mustResetLookupEnvironment = true;
    }

    private void retrieveInnerDeclarations(Declaration declaration, List<Declaration> allDeclarations) {
        List<Declaration> members = declaration.getMembers();
        allDeclarations.addAll(members);
        for (Declaration member : members) {
            retrieveInnerDeclarations(member, allDeclarations);
        }
    }

    private final Map<String, CeylonDeclaration> sourceDeclarations = new TreeMap<String, CeylonDeclaration>();

    public synchronized Set<String> getSourceDeclarations() {
        Set<String> declarations = new HashSet<String>();
        declarations.addAll(sourceDeclarations.keySet());
        return declarations;
    }

    public static interface SourceFileObjectManager {
        void setupSourceFileObjects(List<?> treeHolders);
    }

    private SourceFileObjectManager additionalSourceFileObjectsManager = null;

    public synchronized void setSourceFileObjectManager(SourceFileObjectManager manager) {
        additionalSourceFileObjectsManager = manager;
    }

    public synchronized void setupSourceFileObjects(List<?> treeHolders) {
        addSourcePhasedUnits(treeHolders, true);
        if (additionalSourceFileObjectsManager != null) {
            additionalSourceFileObjectsManager.setupSourceFileObjects(treeHolders);
        }
    }

    public synchronized void addSourcePhasedUnits(List<?> treeHolders, final boolean isSourceToCompile) {
        for (Object treeHolder : treeHolders) {
            if (treeHolder instanceof PhasedUnit) {
                final PhasedUnit unit = (PhasedUnit) treeHolder;
                final String pkgName = unit.getPackage().getQualifiedNameString();
                unit.getCompilationUnit().visit(new SourceDeclarationVisitor() {
                    @Override
                    public void loadFromSource(Tree.Declaration decl) {
                        if (decl.getIdentifier() != null) {
                            String name = Util.quoteIfJavaKeyword(decl.getIdentifier().getText());
                            String fqn = getQualifiedName(pkgName, name);
                            if (!sourceDeclarations.containsKey(fqn)) {
                                sourceDeclarations.put(fqn, new CeylonDeclaration(unit, decl, isSourceToCompile));
                            }
                        }
                    }
                });
            }
        }
    }

    public void addSourceArchivePhasedUnits(List<PhasedUnit> sourceArchivePhasedUnits) {
        addSourcePhasedUnits(sourceArchivePhasedUnits, false);
    }

    public synchronized void clearCachesOnPackage(String packageName) {
        List<String> keysToRemove = new ArrayList<String>(classMirrorCache.size());
        for (Entry<String, ClassMirror> element : classMirrorCache.entrySet()) {
            if (element.getValue() == null) {
                String className = element.getKey();
                if (className != null) {
                    String classPackageName = className.replaceAll("\\.[^\\.]+$", "");
                    if (classPackageName.equals(packageName)) {
                        keysToRemove.add(className);
                    }
                }
            }
        }
        for (String keyToRemove : keysToRemove) {
            classMirrorCache.remove(keyToRemove);
        }
        loadedPackages.remove(packageName);
    }

    @Override
    protected LazyValue makeToplevelAttribute(ClassMirror classMirror) {
        if (classMirror instanceof SourceClass) {
            return (LazyValue) (((SourceClass) classMirror).getModelDeclaration());
        }
        return super.makeToplevelAttribute(classMirror);
    }

    @Override
    protected LazyMethod makeToplevelMethod(ClassMirror classMirror) {
        if (classMirror instanceof SourceClass) {
            return (LazyMethod) (((SourceClass) classMirror).getModelDeclaration());
        }
        return super.makeToplevelMethod(classMirror);
    }

    @Override
    protected LazyClass makeLazyClass(ClassMirror classMirror, Class superClass, MethodMirror constructor,
            boolean forTopLevelObject) {
        if (classMirror instanceof SourceClass) {
            return (LazyClass) (((SourceClass) classMirror).getModelDeclaration());
        }
        return super.makeLazyClass(classMirror, superClass, constructor, forTopLevelObject);
    }

    @Override
    protected LazyInterface makeLazyInterface(ClassMirror classMirror) {
        if (classMirror instanceof SourceClass) {
            return (LazyInterface) ((SourceClass) classMirror).getModelDeclaration();
        }
        return super.makeLazyInterface(classMirror);
    }

    public TypeFactory getTypeFactory() {
        return (TypeFactory) typeFactory;
    }

    public synchronized void reset() {
        internalCreate();
        declarationsByName.clear();
        unitsByPackage.clear();
        loadedPackages.clear();
        packageDescriptorsNeedLoading = false;
        classMirrorCache.clear();
    }

    public synchronized void completeFromClasses() {
        for (Entry<String, CeylonDeclaration> entry : sourceDeclarations.entrySet()) {
            CeylonDeclaration declaration = entry.getValue();
            if (mustCompleteFromClasses(declaration)) {
                ClassMirror classMirror = buildClassMirror(entry.getKey());
                if (classMirror == null) {
                    continue;
                }
                final Declaration binaryDeclaration = getOrCreateDeclaration(classMirror, DeclarationType.TYPE,
                        new ArrayList<Declaration>(), new boolean[1]);

                if (binaryDeclaration == null) {
                    continue;
                }

                declaration.getAstDeclaration().visit(new Visitor() {
                    @Override
                    public void visit(Tree.AnyAttribute that) {
                        super.visit(that);
                        Declaration binaryMember = binaryDeclaration.getMember(that.getDeclarationModel().getName(),
                                Collections.<ProducedType>emptyList(), false);
                        if (binaryMember != null) {
                            ProducedType type = ((TypedDeclaration) binaryMember).getType();
                            if (type == null)
                                return;
                            String underlyingType = type.getUnderlyingType();
                            if (underlyingType != null) {
                                ProducedType typeToComplete = that.getDeclarationModel().getType();
                                if (typeToComplete != null) {
                                    typeToComplete.setUnderlyingType(underlyingType);
                                }
                            }
                        }
                    }

                    @Override
                    public void visit(Tree.AttributeSetterDefinition that) {
                        super.visit(that);
                        Declaration binaryMember = binaryDeclaration.getMember(that.getDeclarationModel().getName(),
                                Collections.<ProducedType>emptyList(), false);
                        if (binaryMember != null) {
                            String underlyingType = ((TypedDeclaration) binaryMember).getType().getUnderlyingType();
                            if (underlyingType != null) {
                                ProducedType typeToComplete = that.getDeclarationModel().getType();
                                if (typeToComplete != null) {
                                    typeToComplete.setUnderlyingType(underlyingType);
                                }
                            }

                        }
                    }

                    @Override
                    public void visit(Tree.AnyMethod that) {
                        super.visit(that);
                        Method method = that.getDeclarationModel();
                        Method binaryMethod = (Method) binaryDeclaration.getMember(method.getName(),
                                Collections.<ProducedType>emptyList(), false);
                        if (binaryMethod != null) {
                            String underlyingType = ((TypedDeclaration) binaryMethod).getType().getUnderlyingType();
                            if (underlyingType != null) {
                                ProducedType typeToComplete = that.getDeclarationModel().getType();
                                if (typeToComplete != null) {
                                    typeToComplete.setUnderlyingType(underlyingType);
                                }
                            }

                            Iterator<ParameterList> binaryParamLists = binaryMethod.getParameterLists().iterator();
                            for (ParameterList paramList : method.getParameterLists()) {
                                if (binaryParamLists.hasNext()) {
                                    ParameterList binaryParamList = binaryParamLists.next();
                                    Iterator<Parameter> binaryParams = binaryParamList.getParameters().iterator();
                                    for (Parameter param : paramList.getParameters()) {
                                        if (binaryParams.hasNext()) {
                                            Parameter binaryParam = binaryParams.next();
                                            String paramUnderlyingType = binaryParam.getType().getUnderlyingType();
                                            if (paramUnderlyingType != null) {
                                                ProducedType typeToComplete = param.getType();
                                                if (typeToComplete != null) {
                                                    typeToComplete.setUnderlyingType(paramUnderlyingType);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                });
            }
        }
    }

    private boolean mustCompleteFromClasses(CeylonDeclaration d) {
        return !d.isSourceToCompile()
                && d.getPhasedUnit().getUnit().getPackage().getQualifiedNameString().startsWith("ceylon.language");
    }

}