1 /* 2 * Copyright (C) 2008 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 spechelper; 17 18 import org.eclipse.jdt.core.Flags; 19 import org.eclipse.jdt.core.ICompilationUnit; 20 import org.eclipse.jdt.core.IJavaProject; 21 import org.eclipse.jdt.core.IMethod; 22 import org.eclipse.jdt.core.IType; 23 import org.eclipse.jdt.core.ITypeParameter; 24 import org.eclipse.jdt.core.JavaModelException; 25 import org.eclipse.jdt.core.Signature; 26 import org.eclipse.jdt.core.dom.AST; 27 import org.eclipse.jdt.core.dom.ASTParser; 28 import org.eclipse.jdt.core.dom.ASTVisitor; 29 import org.eclipse.jdt.core.dom.CompilationUnit; 30 import org.eclipse.jdt.core.dom.Expression; 31 import org.eclipse.jdt.core.dom.ITypeBinding; 32 import org.eclipse.jdt.core.dom.SingleMemberAnnotation; 33 import org.eclipse.jdt.internal.ui.JavaPlugin; 34 import org.eclipse.jdt.ui.JavaElementLabelProvider; 35 import org.eclipse.jface.dialogs.IDialogConstants; 36 import org.eclipse.jface.dialogs.MessageDialog; 37 import org.eclipse.swt.widgets.Shell; 38 import org.eclipse.ui.IEditorInput; 39 import org.eclipse.ui.IEditorPart; 40 import org.eclipse.ui.PlatformUI; 41 import org.eclipse.ui.dialogs.ElementListSelectionDialog; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 46 /** 47 * 48 */ 49 public class MethodSelector { 50 51 public String obtainReplacement(String buffer) { 52 IMethod method = selectMethod(); 53 // if user did cancel the selection 54 if (method == null) { 55 return null; 56 } 57 58 // see if we are already in a annotation: 59 // if yes -> only dump the testtarget annotation, not the complete 60 // TestInfo 61 // (could not easily find this out with CompilationUnit, since inserting 62 // a : 63 // broke the AST - maybe use WorkingCopy and so on, 64 // but for now: do it with simple String analysis 65 boolean shortOnly = false; 66 int annotPos = buffer.lastIndexOf("@TestInfo"); 67 // the latest annotation - count "(" ")" pairs - if not the same count 68 // we assume to be in the annotation (H: code compiles fine) 69 if (annotPos != -1) { 70 String sub = buffer.substring(annotPos); 71 // only consider the latest 6 lines for the annotation to occur 72 // (6 = range within which the annotation @TestTarget 73 // must occur, but out of range to reach the annotation from the 74 // previous method - ah i'd prefer working with compilationUnit... 75 String[] lines = sub.split("\n"); 76 for (int i = lines.length - 6; i < lines.length; i++) { 77 String line = lines[i]; 78 if (line.contains("@TestTarget")) { 79 shortOnly = true; 80 } 81 } 82 } 83 84 return generateAnnotation(shortOnly, method); 85 } 86 87 88 private String generateAnnotation(boolean shortOnly, IMethod method) { 89 String[] ptypes = method.getParameterTypes(); 90 String param = ""; 91 for (int i = 0; i < ptypes.length; i++) { 92 String ptype = ptypes[i]; 93 String sig = Signature.toString(ptype); 94 // kind of a hack: convert all Generic Type args to Object, or to 95 // its bound Type 96 if (sig.length() == 1) { 97 ITypeParameter tps = method.getTypeParameter(sig); 98 sig = "Object"; 99 100 if (tps != null && tps.exists()) { 101 try { 102 String[] bounds = tps.getBounds(); 103 if (bounds.length > 0) { 104 sig = bounds[0]; 105 } 106 } catch (JavaModelException e) { 107 e.printStackTrace(); 108 } 109 110 } 111 } 112 // omit type signature 113 sig = sig.replaceAll("<.*>", ""); 114 param += (i > 0 ? ", " : "") + sig + ".class"; 115 } 116 String IND = " "; 117 118 String targ = "@TestTarget(\n" + IND + " methodName = \"" 119 + method.getElementName() + "\",\n" + IND 120 + " methodArgs = {" + param + "}\n" + IND + " )\n"; 121 122 String s; 123 if (shortOnly) { 124 s = targ; 125 } else { 126 127 s = "@TestInfo(\n" + IND + " status = TestStatus.TBR,\n" + IND 128 + " notes = \"\",\n" + IND + " targets = {\n" + IND 129 + " " + targ + IND + "})"; 130 } 131 return s; 132 } 133 134 private IMethod selectMethod() { 135 IEditorPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow() 136 .getActivePage().getActiveEditor(); 137 Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow() 138 .getShell(); 139 IEditorInput ei = part.getEditorInput(); 140 final ICompilationUnit cu = JavaPlugin.getDefault() 141 .getWorkingCopyManager().getWorkingCopy(ei); 142 // cu != null since we register only for java/javadoc completion 143 // proposals 144 ASTParser parser = ASTParser.newParser(AST.JLS3); 145 parser.setSource(cu); 146 parser.setResolveBindings(true); 147 CompilationUnit unit = (CompilationUnit) parser.createAST(null); 148 149 class MHolder { 150 IMethod method; 151 } 152 final MHolder mholder = new MHolder(); 153 154 class FHolder { 155 boolean foundClassAnnotation; 156 } 157 final FHolder fholder = new FHolder(); 158 159 unit.accept(new ASTVisitor() { 160 public boolean visit(SingleMemberAnnotation node) { 161 String name = node.getTypeName().getFullyQualifiedName(); 162 if (!name.equals("TestTargetClass")) { 163 return false; 164 } 165 fholder.foundClassAnnotation = true; 166 Expression targetClassE = node.getValue(); 167 ITypeBinding ty = targetClassE.resolveTypeBinding(); 168 if (ty == null) { 169 return false; 170 } 171 ITypeBinding[] classTypes = ty.getTypeArguments(); 172 if (classTypes.length > 0) { 173 ITypeBinding tp = classTypes[0]; 174 String qname = tp.getQualifiedName(); 175 System.out.println("qname:" + qname); 176 IJavaProject myProject = cu.getJavaProject(); 177 try { 178 IType myType = myProject.findType(qname); 179 if (myType != null) { 180 Shell parent = PlatformUI.getWorkbench() 181 .getActiveWorkbenchWindow().getShell(); 182 ElementListSelectionDialog dialog = new ElementListSelectionDialog( 183 parent, 184 new JavaElementLabelProvider( 185 JavaElementLabelProvider.SHOW_PARAMETERS 186 | JavaElementLabelProvider.SHOW_OVERLAY_ICONS 187 | JavaElementLabelProvider.SHOW_RETURN_TYPE)); 188 // restrict to public/protected methods only 189 IMethod[] allMeth = myType.getMethods(); 190 List<IMethod> pubproMethods = new ArrayList<IMethod>(); 191 for (int i = 0; i < allMeth.length; i++) { 192 IMethod method = allMeth[i]; 193 if ((method.getFlags() & (Flags.AccPublic | Flags.AccProtected)) != 0) { 194 pubproMethods.add(method); 195 } 196 } 197 IMethod[] res = pubproMethods 198 .toArray(new IMethod[pubproMethods.size()]); 199 dialog.setIgnoreCase(true); 200 dialog.setBlockOnOpen(true); 201 dialog.setElements(res);// 202 dialog.setFilter(""); 203 dialog.setTitle(qname); 204 if (dialog.open() != IDialogConstants.CANCEL_ID) { 205 Object[] types = dialog.getResult(); 206 System.out.println("selected:" + types[0]); 207 IMethod method = (IMethod) types[0]; 208 mholder.method = method; 209 210 } else { 211 // System.out.println("cancelled!!"); 212 } 213 } 214 } catch (JavaModelException e) { 215 e.printStackTrace(); 216 } 217 } 218 return true; 219 } 220 }); 221 if (!fholder.foundClassAnnotation) { 222 MessageDialog.openInformation(shell, "Class Annotation missing", 223 "@TestTargetClass(...) is missing"); 224 return null; 225 } 226 return mholder.method; 227 } 228 } 229