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

Source Code of com.google.gdt.eclipse.designer.builders.GwtBuilder

/*******************************************************************************
* 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;

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.builders.participant.MyCompilationParticipant;
import com.google.gdt.eclipse.designer.common.Constants;
import com.google.gdt.eclipse.designer.refactoring.GwtRefactoringUtils;
import com.google.gdt.eclipse.designer.util.Utils;

import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.ast.DomGenerics;
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.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
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.AST;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.ui.actions.OverrideMethodsAction;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.MultiTextEdit;

import org.apache.commons.lang.StringUtils;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* {@link IncrementalProjectBuilder} for building <code>RemoteSErvice</code> Async types.
*
* @author scheglov_ke
* @coverage gwt.builder
*/
public class GwtBuilder extends IncrementalProjectBuilder {
  ////////////////////////////////////////////////////////////////////////////
  //
  // Build time fields
  //
  ////////////////////////////////////////////////////////////////////////////
  private IResourceDelta m_delta;
  private List<IFile> m_serviceFiles;

  ////////////////////////////////////////////////////////////////////////////
  //
  // Build
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  @SuppressWarnings("rawtypes")
  protected IProject[] build(int kind, Map args, final IProgressMonitor _monitor)
      throws CoreException {
    ExecutionUtils.runLog(new RunnableEx() {
      public void run() throws Exception {
        buildEx(_monitor);
      }
    });
    _monitor.done();
    return null;
  }

  private void buildEx(final IProgressMonitor _monitor) throws Exception {
    IProgressMonitor monitor = new SubProgressMonitor(_monitor, 3);
    // prepare state
    {
      IProject project = getProject();
      m_delta = getDelta(project);
    }
    // respond to delta
    if (m_delta != null) {
      if (Activator.getStore().getBoolean(Constants.P_BUILDER_GENERATE_ASYNC)) {
        monitor.beginTask("Building Remote Service's...", 2);
        //
        monitor.subTask("Looking for RemoteService Files");
        findRemoteServiceFiles();
        monitor.worked(1);
        //
        monitor.subTask("Updating RemoteService's Async and Impl parts");
        updateRemoteServices();
        monitor.worked(1);
      }
      {
        monitor.beginTask("Checking *.GWT.XML modifications...", 1);
        checkModuleFileModification();
        monitor.worked(1);
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Async interfaces generation
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Prepares {@link IFile}'s with <code>RemoveService</code> for current build type (incremental or
   * full).
   */
  private void findRemoteServiceFiles() throws CoreException {
    m_serviceFiles = Lists.newArrayList();
    // visit delta
    IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
      public boolean visit(IResourceDelta delta) throws CoreException {
        IResource resource = delta.getResource();
        switch (resource.getType()) {
          case IResource.PROJECT :
            IProject project = (IProject) resource;
            // we are interesting only in GWT projects
            return Utils.isGWTProject(project);
          case IResource.FILE :
            if (Utils.isRemoteService(resource)) {
              m_serviceFiles.add((IFile) resource);
            }
        }
        return true;
      }
    };
    m_delta.accept(visitor);
  }

  /**
   * Updates Async and Impl parts for each modified <code>RemoteService</code>.
   */
  private void updateRemoteServices() throws Exception {
    for (IFile serviceFile : m_serviceFiles) {
      ICompilationUnit serviceUnit = (ICompilationUnit) JavaCore.create(serviceFile);
      IPackageFragment servicePackage = (IPackageFragment) serviceUnit.getParent();
      //
      generateAsync(servicePackage, serviceUnit);
      updateImpl(servicePackage, serviceUnit);
    }
  }

  /**
   * Generates Async type for given <code>RemoteService</code>.
   */
  private void generateAsync(IPackageFragment servicePackage, ICompilationUnit serviceUnit)
      throws Exception {
    IJavaProject javaProject = serviceUnit.getJavaProject();
    // parse service unit
    CompilationUnit serviceRoot = Utils.parseUnit(serviceUnit);
    // prepare AST and start modifications recording
    AST ast = serviceRoot.getAST();
    serviceRoot.recordModifications();
    // modify imports (-com.google.gwt.*, -*Exception, +AsyncCallback)
    {
      List<ImportDeclaration> imports = DomGenerics.imports(serviceRoot);
      // remove useless imports
      for (Iterator<ImportDeclaration> I = imports.iterator(); I.hasNext();) {
        ImportDeclaration importDeclaration = I.next();
        String importName = importDeclaration.getName().getFullyQualifiedName();
        if (importName.startsWith("com.google.gwt.user.client.rpc.")
            || importName.equals("com.google.gwt.core.client.GWT")
            || importName.endsWith("Exception")) {
          I.remove();
        }
      }
    }
    // add Async to the name
    TypeDeclaration serviceType = (TypeDeclaration) serviceRoot.types().get(0);
    String remoteServiceAsyncName = serviceType.getName().getIdentifier() + "Async";
    serviceType.setName(serviceRoot.getAST().newSimpleName(remoteServiceAsyncName));
    // update interfaces
    updateInterfacesOfAsync(javaProject, serviceRoot, serviceType);
    // change methods, fields and inner classes
    {
      List<BodyDeclaration> bodyDeclarations = DomGenerics.bodyDeclarations(serviceType);
      for (Iterator<BodyDeclaration> I = bodyDeclarations.iterator(); I.hasNext();) {
        BodyDeclaration bodyDeclaration = I.next();
        if (bodyDeclaration instanceof MethodDeclaration) {
          MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
          // make return type void
          Type returnType;
          {
            returnType = methodDeclaration.getReturnType2();
            methodDeclaration.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID));
          }
          // process JavaDoc
          {
            Javadoc javadoc = methodDeclaration.getJavadoc();
            if (javadoc != null) {
              List<TagElement> tags = DomGenerics.tags(javadoc);
              for (Iterator<TagElement> tagIter = tags.iterator(); tagIter.hasNext();) {
                TagElement tag = tagIter.next();
                if ("@gwt.typeArgs".equals(tag.getTagName())) {
                  tagIter.remove();
                } else if ("@return".equals(tag.getTagName())) {
                  if (!tag.fragments().isEmpty()) {
                    tag.setTagName("@param callback the callback to return");
                  } else {
                    tagIter.remove();
                  }
                } else if ("@wbp.gwt.Request".equals(tag.getTagName())) {
                  tagIter.remove();
                  addImport(serviceRoot, "com.google.gwt.http.client.Request");
                  methodDeclaration.setReturnType2(ast.newSimpleType(ast.newName("Request")));
                }
              }
              // remove empty JavaDoc
              if (tags.isEmpty()) {
                methodDeclaration.setJavadoc(null);
              }
            }
          }
          // add AsyncCallback parameter
          {
            addImport(serviceRoot, "com.google.gwt.user.client.rpc.AsyncCallback");
            // prepare "callback" type
            Type callbackType;
            {
              callbackType = ast.newSimpleType(ast.newName("AsyncCallback"));
              Type objectReturnType = getObjectType(returnType);
              ParameterizedType parameterizedType = ast.newParameterizedType(callbackType);
              DomGenerics.typeArguments(parameterizedType).add(objectReturnType);
              callbackType = parameterizedType;
            }
            // prepare "callback" parameter
            SingleVariableDeclaration asyncCallback = ast.newSingleVariableDeclaration();
            asyncCallback.setType(callbackType);
            asyncCallback.setName(ast.newSimpleName("callback"));
            // add "callback" parameter
            DomGenerics.parameters(methodDeclaration).add(asyncCallback);
          }
          // remove throws
          methodDeclaration.thrownExceptions().clear();
        } else if (bodyDeclaration instanceof FieldDeclaration
            || bodyDeclaration instanceof TypeDeclaration) {
          // remove the fields and inner classes
          I.remove();
        }
      }
    }
    // apply modifications to prepare new source code
    String newSource;
    {
      String source = serviceUnit.getBuffer().getContents();
      Document document = new Document(source);
      // prepare text edits
      MultiTextEdit edits =
          (MultiTextEdit) serviceRoot.rewrite(document, javaProject.getOptions(true));
      removeAnnotations(serviceType, source, edits);
      // prepare new source code
      edits.apply(document);
      newSource = document.get();
    }
    // update compilation unit
    {
      ICompilationUnit unit =
          servicePackage.createCompilationUnit(
              remoteServiceAsyncName + ".java",
              newSource,
              true,
              null);
      unit.getBuffer().save(null, true);
    }
  }

  private static void addImport(CompilationUnit compilationUnit, String qualifiedName) {
    AST ast = compilationUnit.getAST();
    List<ImportDeclaration> imports = DomGenerics.imports(compilationUnit);
    // check for existing ImportDeclaration
    for (ImportDeclaration importDeclaration : imports) {
      if (importDeclaration.getName().toString().equals(qualifiedName)) {
        return;
      }
    }
    // add new ImportDeclaration
    ImportDeclaration importDeclaration = ast.newImportDeclaration();
    importDeclaration.setName(ast.newName(qualifiedName));
    imports.add(importDeclaration);
  }

  /**
   * Keeps Async interfaces for services and remove other interfaces.
   */
  private static void updateInterfacesOfAsync(IJavaProject javaProject,
      CompilationUnit serviceUnit,
      TypeDeclaration serviceType) throws Exception {
    String serviceName = AstNodeUtils.getFullyQualifiedName(serviceType, false);
    AST ast = serviceType.getAST();
    for (Iterator<?> I = serviceType.superInterfaceTypes().iterator(); I.hasNext();) {
      Type type = (Type) I.next();
      ITypeBinding typeBinding = AstNodeUtils.getTypeBinding(type);
      if (AstNodeUtils.isSuccessorOf(typeBinding, Constants.CLASS_REMOTE_SERVICE)) {
        String superServiceName = AstNodeUtils.getFullyQualifiedName(typeBinding, false);
        String superAsyncName = superServiceName + "Async";
        if (javaProject.findType(superAsyncName) != null) {
          if (type instanceof SimpleType) {
            {
              SimpleType simpleType = (SimpleType) type;
              String superAsyncNameSimple = CodeUtils.getShortClass(superAsyncName);
              simpleType.setName(ast.newSimpleName(superAsyncNameSimple));
            }
            if (!CodeUtils.isSamePackage(serviceName, superAsyncName)) {
              addImport(serviceUnit, superAsyncName);
            }
            continue;
          }
        }
      }
      I.remove();
    }
  }

  /**
   * Removes annotations of service {@link TypeDeclaration}.
   */
  private static void removeAnnotations(TypeDeclaration serviceType,
      String source,
      MultiTextEdit edits) {
    int typePos = serviceType.getStartPosition();
    {
      Javadoc javadoc = serviceType.getJavadoc();
      if (javadoc != null) {
        typePos = javadoc.getStartPosition() + javadoc.getLength();
        while (Character.isWhitespace(source.charAt(typePos))) {
          typePos++;
        }
      }
    }
    int pureTypePos =
        StringUtils.indexOfAny(
            source,
            new String[]{"public ", "protected ", "class ", "interface "});
    if (pureTypePos != -1 && pureTypePos != typePos) {
      edits.addChild(new DeleteEdit(typePos, pureTypePos - typePos));
    }
  }

  private void updateImpl(IPackageFragment servicePackage, ICompilationUnit serviceUnit)
      throws Exception {
    // find single Impl type
    ICompilationUnit implUnit;
    {
      IType implType = GwtRefactoringUtils.getServiceImplType(serviceUnit.findPrimaryType(), null);
      if (implType == null) {
        return;
      }
      implUnit = implType.getCompilationUnit();
    }
    // prepare AST unit and type
    CompilationUnit implRoot = Utils.parseUnit(implUnit);
    TypeDeclaration implType = (TypeDeclaration) implRoot.types().get(0);
    // use standard JDT operation
    final IWorkspaceRunnable workspaceRunnable =
        OverrideMethodsAction.createRunnable(implRoot, implType.resolveBinding(), null, -1, false);
    // execute in UI because operation works with widgets during apply
    ExecutionUtils.runLogUI(new RunnableEx() {
      public void run() throws Exception {
        workspaceRunnable.run(null);
      }
    });
    implUnit.save(null, true);
    implUnit.getBuffer().save(null, true);
  }

  /**
   * @return the {@link Type} that extends {@link Object}, even if given is primitive.
   */
  private static Type getObjectType(Type type) {
    if (type.isPrimitiveType()) {
      String identifier = ((PrimitiveType) type).getPrimitiveTypeCode().toString();
      if (identifier.equals("int")) {
        identifier = "Integer";
      } else {
        identifier = StringUtils.capitalize(identifier);
      }
      SimpleName typeName = type.getAST().newSimpleName(identifier);
      return type.getAST().newSimpleType(typeName);
    } else {
      return type;
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Recompile on *.gwt.xml modification
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Rebuilds GWT project if any of the *.gwt.xml file was modified. We need this to force out
   * {@link MyCompilationParticipant} to validate again source, because *.gwt.xml may be
   * included/removed some other GWT module and some types become accessible/inaccessible in
   * "source" classpath.
   */
  private void checkModuleFileModification() throws CoreException {
    // optimization for tests
    if (!MyCompilationParticipant.ENABLED) {
      return;
    }
    //
    // prepare projects with changed *.gwt.xml files
    final Set<IProject> projectsToBuild = Sets.newHashSet();
    IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
      public boolean visit(IResourceDelta delta) throws CoreException {
        IResource resource = delta.getResource();
        switch (resource.getType()) {
          case IResource.PROJECT :
            // process only GWT projects
            IProject project = (IProject) resource;
            return Utils.isGWTProject(project);
          case IResource.FOLDER : {
            // process only resources in "src" folders
            IJavaProject javaProject = JavaCore.create(getProject());
            return javaProject.isOnClasspath(resource);
          }
          case IResource.FILE :
            if (Utils.getExactModule(resource) != null) {
              projectsToBuild.add(resource.getProject());
            }
        }
        return true;
      }
    };
    m_delta.accept(visitor);
    // run "Clean" job for found projects
    if (!projectsToBuild.isEmpty()) {
      WorkspaceJob cleanJob = new WorkspaceJob("GWT project rebuild") {
        @Override
        public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
          for (IProject project : projectsToBuild) {
            project.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor());
          }
          return Status.OK_STATUS;
        }
      };
      cleanJob.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule());
      cleanJob.setUser(true);
      cleanJob.schedule();
    }
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.builders.GwtBuilder

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.