1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.currysrc.processors; 17 18 import com.google.currysrc.api.process.Context; 19 import com.google.currysrc.api.process.Processor; 20 import com.google.currysrc.api.process.ast.TypeLocator; 21 import java.util.List; 22 import org.eclipse.jdt.core.dom.AST; 23 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 24 import org.eclipse.jdt.core.dom.CompilationUnit; 25 import org.eclipse.jdt.core.dom.FieldDeclaration; 26 import org.eclipse.jdt.core.dom.MethodDeclaration; 27 import org.eclipse.jdt.core.dom.Modifier; 28 import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; 29 import org.eclipse.jdt.core.dom.TypeDeclaration; 30 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 31 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 32 33 /** 34 * Add a default constructor to a white list of classes. 35 */ 36 public class AddDefaultConstructor implements Processor { 37 38 /** 39 * The visibility modifier keywords. 40 */ 41 private static final ModifierKeyword[] VISIBILITY_MODIFIERS = new ModifierKeyword[]{ 42 ModifierKeyword.PUBLIC_KEYWORD, 43 ModifierKeyword.PRIVATE_KEYWORD, ModifierKeyword.PROTECTED_KEYWORD}; 44 45 private final List<TypeLocator> whitelist; 46 private Listener listener; 47 48 public interface Listener { 49 50 /** 51 * Called when a default constructor is added to a class. 52 * 53 * @param locator the locator for the modified class. 54 * @param typeDeclaration the class that was modified. 55 */ onAddDefaultConstructor(TypeLocator locator, TypeDeclaration typeDeclaration)56 void onAddDefaultConstructor(TypeLocator locator, TypeDeclaration typeDeclaration); 57 } 58 AddDefaultConstructor(List<TypeLocator> whitelist)59 public AddDefaultConstructor(List<TypeLocator> whitelist) { 60 this.whitelist = whitelist; 61 this.listener = (l, typeDeclaration) -> {}; 62 } 63 setListener(Listener listener)64 public void setListener(Listener listener) { 65 this.listener = listener; 66 } 67 68 @Override process(Context context, CompilationUnit cu)69 public void process(Context context, CompilationUnit cu) { 70 final ASTRewrite rewrite = context.rewrite(); 71 for (TypeLocator typeLocator : whitelist) { 72 AbstractTypeDeclaration abstractTypeDeclaration = typeLocator.find(cu); 73 if (abstractTypeDeclaration instanceof TypeDeclaration) { 74 TypeDeclaration typeDeclaration = (TypeDeclaration) abstractTypeDeclaration; 75 addDefaultConstructor(rewrite, typeDeclaration); 76 listener.onAddDefaultConstructor(typeLocator, typeDeclaration); 77 } 78 } 79 } 80 addDefaultConstructor(ASTRewrite rewrite, TypeDeclaration node)81 private void addDefaultConstructor(ASTRewrite rewrite, TypeDeclaration node) { 82 AST ast = rewrite.getAST(); 83 MethodDeclaration method = ast.newMethodDeclaration(); 84 method.setConstructor(true); 85 // Copy the class name as a constructor must have the same name as the class. 86 method.setName(ast.newSimpleName(node.getName().getIdentifier())); 87 88 // The default constructor has the same visibility as its containing class. 89 int modifiers = node.getModifiers(); 90 @SuppressWarnings("unchecked") List<Modifier> methodModifiers = method.modifiers(); 91 for (ModifierKeyword keyword : VISIBILITY_MODIFIERS) { 92 if ((modifiers & keyword.toFlagValue()) != 0) { 93 methodModifiers.add(ast.newModifier(keyword)); 94 } 95 } 96 97 // Create an empty body. 98 method.setBody(ast.newBlock()); 99 100 ListRewrite listRewrite = rewrite.getListRewrite(node, node.getBodyDeclarationsProperty()); 101 102 MethodDeclaration[] methods = node.getMethods(); 103 if (methods.length > 0) { 104 // Insert before the first method. 105 listRewrite.insertBefore(method, methods[0], null); 106 } else { 107 FieldDeclaration[] fields = node.getFields(); 108 if (fields.length > 0) { 109 // Insert after the last field. 110 listRewrite.insertAfter(method, fields[fields.length - 1], null); 111 } else { 112 // Insert at the beginning of the class. 113 listRewrite.insertFirst(method, null); 114 } 115 } 116 } 117 } 118