Package com.google.gdt.eclipse.designer.builders.participant

Source Code of com.google.gdt.eclipse.designer.builders.participant.MyCompilationParticipant

/*******************************************************************************
* 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);
      }
    }
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.builders.participant.MyCompilationParticipant

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.