com.google.gdt.eclipse.designer.builders.participant.MyCompilationParticipant.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gdt.eclipse.designer.builders.participant.MyCompilationParticipant.java

Source

/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.google.gdt.eclipse.designer.builders.participant;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gdt.eclipse.designer.Activator;
import com.google.gdt.eclipse.designer.common.Constants;
import com.google.gdt.eclipse.designer.model.module.ModuleElement;
import com.google.gdt.eclipse.designer.util.ModuleDescription;
import com.google.gdt.eclipse.designer.util.ModuleVisitor;
import com.google.gdt.eclipse.designer.util.Utils;
import com.google.gdt.eclipse.designer.util.resources.IResourcesProvider;

import org.eclipse.wb.internal.core.utils.IOUtils2;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.io.InputStream;
import java.util.List;
import java.util.Set;

/**
 * Compilation participant that check that all imported packages are "source" packages of some
 * inherited module.
 * 
 * @author scheglov_ke
 * @coverage gwt.compilation.participant
 */
public final class MyCompilationParticipant extends AbstractCompilationParticipant {
    public static boolean ENABLED = true;
    /**
     * If the <code>com.google.gdt.eclipse.designer.wizards</code> plug-in is present, then it
     * indicates that the user has the pre-GPE integrated version of GWT Designer.
     */
    public static boolean WIZARD_PLUGIN_PRESENT = Platform
            .getBundle("com.google.gdt.eclipse.designer.wizards") != null;
    public static final String MARKER_ID = "com.google.gdt.eclipse.designer.problem";

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public MyCompilationParticipant() {
        super(MARKER_ID);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Active check
    //
    ////////////////////////////////////////////////////////////////////////////
    @Override
    public boolean isActive(IJavaProject project) {
        return ENABLED && WIZARD_PLUGIN_PRESENT && Utils.isGWTProject(project);
    }

    @Override
    public boolean isAnnotationProcessor() {
        return true;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Compiling
    //
    ////////////////////////////////////////////////////////////////////////////
    @Override
    protected void addMarkers(List<MarkerInfo> newMarkers, IFile file, ICompilationUnit modelUnit,
            CompilationUnit astUnit) throws Exception {
        // look if checking is enabled
        {
            IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore();
            if (!preferenceStore.getBoolean(Constants.P_BUILDER_CHECK_CLIENT_CLASSPATH)) {
                return;
            }
        }
        // check if unit is in source package
        if (!Utils.isModuleSourcePackage((IPackageFragment) modelUnit.getParent())) {
            return;
        }
        //
        {
            ModuleDescription moduleDescription = Utils.getSingleModule(file);
            if (moduleDescription != null) {
                // prepare document
                IDocument document;
                {
                    String contents = IOUtils2.readString(file);
                    document = new Document(contents);
                }
                // add error markers for not imported types
                IResourcesProvider resourcesProvider = moduleDescription.getResourcesProvider();
                try {
                    addMarkers_notImportedTypes(newMarkers, resourcesProvider, moduleDescription, astUnit, file,
                            document);
                } finally {
                    resourcesProvider.dispose();
                }
            }
        }
    }

    /**
     * Adds error markers for types that are not visible in inherited "source" packages.
     */
    private void addMarkers_notImportedTypes(final List<MarkerInfo> newMarkers,
            final IResourcesProvider resourcesProvider, ModuleDescription moduleDescription,
            CompilationUnit astUnit, final IFile file, final IDocument document) throws Exception {
        final IJavaProject javaProject = JavaCore.create(file.getProject());
        // prepare list of source packages
        final List<SourcePackageDescriptor> sourcePackages = Lists.newArrayList();
        ModuleVisitor.accept(moduleDescription, new ModuleVisitor() {
            @Override
            public void visitSourcePackage(ModuleElement module, String packageName, boolean superSource)
                    throws Exception {
                sourcePackages.add(new SourcePackageDescriptor(packageName, superSource));
            }
        });
        // validate all types in CompilationUnit
        astUnit.accept(new ASTVisitor() {
            private final Set<String> m_validClasses = Sets.newTreeSet();
            private final Set<String> m_invalidClasses = Sets.newTreeSet();

            @Override
            public boolean visit(SingleMemberAnnotation node) {
                return false;
            }

            @Override
            public boolean visit(NormalAnnotation node) {
                return false;
            }

            @Override
            public void postVisit(final ASTNode node) {
                ExecutionUtils.runIgnore(new RunnableEx() {
                    public void run() throws Exception {
                        postVisitEx(node);
                    }
                });
            }

            private void postVisitEx(ASTNode node) throws Exception {
                // ignore imports
                if (AstNodeUtils.getEnclosingNode(node, ImportDeclaration.class) != null) {
                    return;
                }
                // check known cases
                if (node instanceof SimpleType) {
                    SimpleType simpleType = (SimpleType) node;
                    ITypeBinding typeBinding = simpleType.resolveBinding();
                    checkNode(node, typeBinding);
                } else if (node instanceof SimpleName) {
                    SimpleName simpleName = (SimpleName) node;
                    if (simpleName.resolveBinding().getKind() == IBinding.TYPE
                            && node.getLocationInParent() == MethodInvocation.EXPRESSION_PROPERTY) {
                        ITypeBinding typeBinding = simpleName.resolveTypeBinding();
                        checkNode(node, typeBinding);
                    }
                }
            }

            private void checkNode(ASTNode node, ITypeBinding typeBinding) throws Exception {
                if (typeBinding != null) {
                    // ignore generics type variable
                    if (typeBinding.isTypeVariable()) {
                        return;
                    }
                    // only top level types can be found as source
                    while (typeBinding.getDeclaringClass() != null) {
                        typeBinding = typeBinding.getDeclaringClass();
                    }
                    // check this type
                    String typeName = AstNodeUtils.getFullyQualifiedName(typeBinding, true);
                    if (isSecondarySourceType(typeName)) {
                        return;
                    }
                    checkClass(node, typeName);
                }
            }

            private boolean isSecondarySourceType(String typeName) throws Exception {
                // usually secondary type can not be found using this way
                IType type = javaProject.findType(typeName);
                if (type == null) {
                    return true;
                }
                // "secondary source type" has compilation unit
                ICompilationUnit compilationUnit = type.getCompilationUnit();
                if (compilationUnit == null) {
                    return false;
                }
                // check if type name in same as unit name
                String unitName = compilationUnit.getElementName();
                unitName = StringUtils.removeEnd(unitName, ".java");
                return !typeName.endsWith("." + unitName);
            }

            /**
             * Check that class with given name is defined in this or inherited module.
             */
            private void checkClass(ASTNode node, String className) throws Exception {
                if (!isValid(className)) {
                    markAsInvalid(node, className);
                }
            }

            /**
             * @return <code>true</code> if given class is valid.
             */
            private boolean isValid(String className) {
                // check cached valid classes
                if (m_validClasses.contains(className)) {
                    return true;
                }
                // check cached invalid classes
                if (m_invalidClasses.contains(className)) {
                    return false;
                }
                // no information in caches, do checks 
                for (SourcePackageDescriptor sourcePackageDescriptor : sourcePackages) {
                    if (sourcePackageDescriptor.isValidClass(resourcesProvider, className)) {
                        m_validClasses.add(className);
                        return true;
                    }
                }
                // mark as invalid
                m_invalidClasses.add(className);
                return false;
            }

            private void markAsInvalid(ASTNode node, String className) throws Exception {
                String message = className + " can not be found in source packages. "
                        + "Check the inheritance chain from your module; "
                        + "it may not be inheriting a required module or a module "
                        + "may not be adding its source path entries properly.";
                String moduleNameToImport = getEnclosingModule(resourcesProvider, className);
                newMarkers.add(createMarkerInfo_importModule(file, document, node.getStartPosition(),
                        node.getLength(), message, moduleNameToImport));
            }
        });
    }

    /**
     * @return the name of GWT module that contains given class, may be <code>null</code>.
     */
    private static String getEnclosingModule(IResourcesProvider resourcesProvider, String className)
            throws Exception {
        String packageName = CodeUtils.getPackage(className);
        while (packageName.length() != 0) {
            List<String> files = resourcesProvider.listFiles(packageName.replace('.', '/'));
            for (String file : files) {
                if (file.indexOf('/') == -1 && file.endsWith(".gwt.xml")) {
                    String shortModuleName = StringUtils.substring(file, 0, -".gwt.xml".length());
                    return packageName + "." + shortModuleName;
                }
            }
            // go up
            packageName = CodeUtils.getPackage(packageName);
        }
        return null;
    }

    private static MarkerInfo createMarkerInfo_importModule(IResource resource, IDocument document, int start,
            int length, String message, String moduleNameToImport) throws BadLocationException {
        int line = document.getLineOfOffset(start);
        return new MarkerInfoImportModule(resource, start, start + length, line, IMarker.SEVERITY_ERROR, message,
                moduleNameToImport);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Source package
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Descriptor for source package.
     */
    private static final class SourcePackageDescriptor {
        private final String m_packageName;
        private final boolean m_superSource;

        ////////////////////////////////////////////////////////////////////////////
        //
        // Constructor
        //
        ////////////////////////////////////////////////////////////////////////////
        public SourcePackageDescriptor(String packageName, boolean superSource) {
            m_packageName = packageName;
            m_superSource = superSource;
        }

        ////////////////////////////////////////////////////////////////////////////
        //
        // Access
        //
        ////////////////////////////////////////////////////////////////////////////
        /**
         * Checks if class with given name exists in this package.
         */
        public boolean isValidClass(IResourcesProvider resourcesProvider, String className) {
            // prepare path to the source of class with given name
            String sourceFilePath;
            if (m_superSource) {
                sourceFilePath = (m_packageName + "." + className).replace('.', '/') + ".java";
            } else {
                if (!className.startsWith(m_packageName)) {
                    return false;
                }
                sourceFilePath = className.replace('.', '/') + ".java";
            }
            // check that we can find source for class with given name
            InputStream resourceAsStream = null;
            try {
                resourceAsStream = resourcesProvider.getResourceAsStream(sourceFilePath);
                return resourceAsStream != null;
            } catch (Throwable e) {
                return false;
            } finally {
                IOUtils.closeQuietly(resourceAsStream);
            }
        }
    }
}