| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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.gwt.dev.javac; |
| |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.Annotation; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Handles the removal of GwtIncompatible annotated classes and members. |
| */ |
| public class GwtIncompatiblePreprocessor { |
| /** |
| * Checks whether GwtIncompatible is in the array of {@code Annotation}. |
| * |
| * @param annotations an (possible null) array of {@code Annotation} |
| * @return {@code true} if there is an annotation of class {@code *.GwtIncompatible} in |
| * array. {@code false} otherwise. |
| */ |
| private static boolean hasGwtIncompatibleAnnotation(Annotation[] annotations) { |
| if (annotations == null) { |
| return false; |
| } |
| for (Annotation ann : annotations) { |
| String typeName = new String(ann.type.getLastToken()); |
| if (typeName.equals("GwtIncompatible")) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Process all members of a type to remove any @GwtIncompatible one. |
| */ |
| private static void processMembers(TypeDeclaration tyDecl) { |
| processTypes(tyDecl.memberTypes); |
| processMethods(tyDecl); |
| processFields(tyDecl); |
| } |
| |
| /** |
| * Remove @GwtIncompatible classes, members and recursively remove @GwtIncompatible |
| * inner classes. A dummy empty stub is retained for @GwtIncompatible classes. |
| */ |
| private static void processTypes(TypeDeclaration[] types) { |
| if (types == null) { |
| return; |
| } |
| for (TypeDeclaration tyDecl : types) { |
| if (!hasGwtIncompatibleAnnotation(tyDecl.annotations)) { |
| processMembers(tyDecl); |
| } else { |
| // Leave the empty class. |
| stripAllMembers(tyDecl); |
| } |
| } |
| } |
| |
| /** |
| * Modifies the methods array of type {@code tyDecl} to remove any GwtIncompatible methods. |
| */ |
| private static void processMethods(TypeDeclaration tyDecl) { |
| if (tyDecl.methods == null) { |
| return; |
| } |
| |
| List<AbstractMethodDeclaration> newMethods = new ArrayList<AbstractMethodDeclaration>(); |
| for (AbstractMethodDeclaration methodDecl : tyDecl.methods) { |
| if (!hasGwtIncompatibleAnnotation(methodDecl.annotations)) { |
| newMethods.add(methodDecl); |
| } |
| } |
| |
| if (newMethods.size() != tyDecl.methods.length) { |
| tyDecl.methods = newMethods.isEmpty() ? null : newMethods.toArray( |
| new AbstractMethodDeclaration[newMethods.size()]); |
| } |
| } |
| |
| /** |
| * Modifies the fields array of type {@code tyDecl} to remove any GwtIncompatible fields. |
| */ |
| private static void processFields(TypeDeclaration tyDecl) { |
| if (tyDecl.fields == null) { |
| return; |
| } |
| |
| List<FieldDeclaration> newFields = new ArrayList<FieldDeclaration>(); |
| for (FieldDeclaration fieldDecl : tyDecl.fields) { |
| if (!hasGwtIncompatibleAnnotation(fieldDecl.annotations)) { |
| newFields.add(fieldDecl); |
| } |
| } |
| |
| if (newFields.size() != tyDecl.fields.length) { |
| tyDecl.fields = newFields.isEmpty() ? null : newFields.toArray(new FieldDeclaration[newFields |
| .size()]); |
| } |
| } |
| |
| /** |
| * Process inner classes, methods and fields from all anonymous inner classes. |
| * |
| * <p>Anonymous inner classes are represented inside expressions. Traverse the JDT AST removing |
| * anonymous inner classes in one go. |
| */ |
| private static void processAllAnonymousInnerClasses(CompilationUnitDeclaration cud) { |
| ASTVisitor visitor = new ASTVisitor() { |
| // Anonymous types are represented within the AST expression that creates the it. |
| @Override |
| public void endVisit(QualifiedAllocationExpression qualifiedAllocationExpression, |
| BlockScope scope) { |
| if (qualifiedAllocationExpression.anonymousType != null) { |
| processMembers(qualifiedAllocationExpression.anonymousType); |
| } |
| } |
| }; |
| cud.traverse(visitor, cud.scope); |
| } |
| |
| /** |
| * Removes all members of a class to leave it as an empty stub. |
| */ |
| private static void stripAllMembers(TypeDeclaration tyDecl) { |
| if (TypeDeclaration.kind(tyDecl.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL) { |
| // Do not modify annotations at all. |
| return; |
| } |
| tyDecl.superclass = null; |
| tyDecl.superInterfaces = null; |
| tyDecl.annotations = null; |
| tyDecl.methods = null; |
| tyDecl.memberTypes = null; |
| tyDecl.fields = null; |
| if (TypeDeclaration.kind(tyDecl.modifiers) == TypeDeclaration.CLASS_DECL) { |
| // Create a default constructor so that the class is proper. |
| ConstructorDeclaration constructor = tyDecl.createDefaultConstructor(true, true); |
| // Mark only constructor as private so that it can not be instantiated. |
| constructor.modifiers = ClassFileConstants.AccPrivate; |
| // Clear a bit that is used for marking the constructor as default as it makes JDT |
| // assume that the constructor is public. |
| constructor.bits &= ~ASTNode.IsDefaultConstructor; |
| // Mark the class as final so that it can not be extended. |
| tyDecl.modifiers |= ClassFileConstants.AccFinal; |
| tyDecl.modifiers &= ~ClassFileConstants.AccAbstract; |
| } |
| } |
| |
| /** |
| * Preprocess the compilation unit to remove @GwtIncompatible classes and members. |
| */ |
| public static void preproccess(CompilationUnitDeclaration cud) { |
| processTypes(cud.types); |
| processAllAnonymousInnerClasses(cud); |
| } |
| } |