Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 Google Inc. |
| 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 | |
| 17 | package com.google.doclava; |
| 18 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 19 | import com.google.doclava.parser.JavaLexer; |
| 20 | import com.google.doclava.parser.JavaParser; |
| 21 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 22 | import org.antlr.runtime.ANTLRFileStream; |
| 23 | import org.antlr.runtime.CommonToken; |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 24 | import org.antlr.runtime.CommonTokenStream; |
| 25 | import org.antlr.runtime.RecognitionException; |
| 26 | import org.antlr.runtime.debug.ParseTreeBuilder; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 27 | import org.antlr.runtime.tree.ParseTree; |
| 28 | import org.antlr.runtime.tree.Tree; |
| 29 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 30 | import java.io.IOException; |
| 31 | import java.util.ArrayList; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 32 | import java.util.HashMap; |
| 33 | import java.util.HashSet; |
| 34 | import java.util.Iterator; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 35 | |
| 36 | /** |
| 37 | * InfoBuilder parses an individual file and builds Doclava |
| 38 | * objects out of the data within the file. This data is |
| 39 | * stored within a global cache for later use. |
| 40 | */ |
| 41 | public class InfoBuilder { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 42 | private PackageInfo mPackage; |
| 43 | private ArrayList<String> mImports; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 44 | private HashSet<String> mClassNames; |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 45 | private String mFilename; // TODO - remove this eventually |
| 46 | private ClassInfo mRootClass; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 47 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 48 | public InfoBuilder(String filename) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 49 | mImports = new ArrayList<String>(); |
| 50 | mImports.add("java.lang.*"); // should allow us to resolve this properly, eventually |
| 51 | // alternatively, we could add everything from java.lang.* |
| 52 | // but that would probably be too brittle |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 53 | mClassNames = new HashSet<String>(); |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 54 | mFilename = filename; |
| 55 | } |
| 56 | |
| 57 | @Override |
| 58 | public String toString() { |
| 59 | return mFilename; |
| 60 | } |
| 61 | |
| 62 | public void parseFile() { |
| 63 | JavaLexer lex; |
| 64 | try { |
| 65 | lex = new JavaLexer(new ANTLRFileStream(mFilename, "UTF8")); |
| 66 | |
| 67 | CommonTokenStream tokens = new CommonTokenStream(lex); |
| 68 | |
| 69 | // create the ParseTreeBuilder to build a parse tree |
| 70 | // much easier to parse than ASTs |
| 71 | ParseTreeBuilder builder = new ParseTreeBuilder("compilationUnit"); |
| 72 | JavaParser g = new JavaParser(tokens, builder); |
| 73 | |
| 74 | g.compilationUnit(); |
| 75 | ParseTree tree = builder.getTree(); |
| 76 | |
| 77 | lex = null; |
| 78 | tokens = null; |
| 79 | builder = null; |
| 80 | g = null; |
| 81 | |
| 82 | parseFile(tree); |
| 83 | |
| 84 | } catch (IOException e1) { |
| 85 | e1.printStackTrace(); |
| 86 | } catch (RecognitionException e) { |
| 87 | e.printStackTrace(); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | public static void resolve() { |
| 92 | Caches.resolve(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | // All of the print functions exist for debugging alone. |
| 96 | public void printStuff() { |
| 97 | System.out.println(mPackage.name() + "\n"); |
| 98 | |
| 99 | printList(mImports); |
| 100 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 101 | Caches.printResolutions(); |
| 102 | } |
| 103 | |
| 104 | private void printList(ArrayList<String> list) { |
| 105 | for (String value : list) { |
| 106 | System.out.println(value); |
| 107 | } |
| 108 | |
| 109 | System.out.println(); |
| 110 | } |
| 111 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 112 | public static void printClassInfo(ClassInfo cl) { |
| 113 | System.out.print("Class: " + cl.toString()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 114 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 115 | printTypeVariables(cl.type()); |
| 116 | |
| 117 | System.out.println(); |
| 118 | |
| 119 | System.out.println(cl.comment().mText); |
| 120 | |
| 121 | if (!cl.annotations().isEmpty()) { |
| 122 | System.out.println("\nAnnotations:"); |
| 123 | printAnnotations(cl.annotations()); |
| 124 | } |
| 125 | |
| 126 | if (cl.superclass() != null) { |
| 127 | System.out.print("Superclass: " + cl.superclass().qualifiedName()); |
| 128 | printTypeVariables(cl.superclassType()); |
| 129 | System.out.println(); |
| 130 | } |
| 131 | |
| 132 | if (!cl.realInterfaces().isEmpty()) { |
| 133 | System.out.println("\nInterfaces Implemented:"); |
| 134 | Iterator<TypeInfo> it = cl.realInterfaceTypes().iterator(); |
| 135 | for (ClassInfo cls : cl.realInterfaces()) { |
| 136 | TypeInfo outerType = it.next(); |
| 137 | if (cls == null) { |
| 138 | System.out.print(outerType.simpleTypeName()); |
| 139 | } else { |
| 140 | System.out.print(cls.qualifiedName()); |
| 141 | } |
| 142 | |
| 143 | printTypeVariables(outerType); |
| 144 | |
| 145 | System.out.println(); |
| 146 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 147 | |
| 148 | System.out.println(); |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 149 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 150 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 151 | if (!cl.allSelfFields().isEmpty()) { |
| 152 | System.out.println("\nFields:"); |
| 153 | for (FieldInfo f : cl.allSelfFields()) { |
| 154 | if (f != cl.allSelfFields().get(0)) { |
| 155 | System.out.println(); |
| 156 | } |
| 157 | System.out.println(f.comment().mText); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 158 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 159 | printAnnotations(f.annotations()); |
| 160 | printTypeName(f.type()); |
| 161 | |
| 162 | System.out.print(" " + f.name()); |
| 163 | |
| 164 | if (f.constantValue() != null) { |
| 165 | System.out.println(": " + f.constantValue()); |
| 166 | } else if (f.hasValue()) { |
| 167 | System.out.println(": has some value"); |
| 168 | } else { |
| 169 | System.out.println(); |
| 170 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 171 | } |
| 172 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 173 | System.out.println(); |
| 174 | } |
| 175 | |
| 176 | if (cl.enumConstants() != null && !cl.enumConstants().isEmpty()) { |
| 177 | System.out.println("\nEnum Constants:"); |
| 178 | for (FieldInfo f : cl.enumConstants()) { |
| 179 | if (f != cl.enumConstants().get(0)) { |
| 180 | System.out.println(); |
| 181 | } |
| 182 | System.out.println(f.comment().mText); |
| 183 | printAnnotations(f.annotations()); |
| 184 | System.out.print(f.type().simpleTypeName() + " " + f.name()); |
| 185 | |
| 186 | if (f.constantValue() != null) { |
| 187 | System.out.println(": " + f.constantValue()); |
| 188 | } else { |
| 189 | System.out.println(); |
| 190 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 191 | } |
| 192 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 193 | System.out.println(); |
| 194 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 195 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 196 | if (!cl.allConstructors().isEmpty()) { |
| 197 | System.out.println("\nConstructors:"); |
| 198 | for (MethodInfo m : cl.allConstructors()) { |
| 199 | if (m != cl.allConstructors().get(0)) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 200 | System.out.println(); |
| 201 | } |
| 202 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 203 | System.out.println(m.comment().mText); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 204 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 205 | printAnnotations(m.annotations()); |
| 206 | if (m.getTypeParameters() != null) { |
| 207 | printTypeVariableList(m.getTypeParameters()); |
| 208 | System.out.print(" "); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 209 | } |
| 210 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 211 | System.out.println(m.name() + m.flatSignature()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 212 | } |
| 213 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 214 | System.out.println(); |
| 215 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 216 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 217 | if (!cl.allSelfMethods().isEmpty()) { |
| 218 | System.out.println("\nMethods:"); |
| 219 | for (MethodInfo m : cl.allSelfMethods()) { |
| 220 | if (m != cl.allSelfMethods().get(0)) { |
| 221 | System.out.println(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 222 | } |
| 223 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 224 | System.out.println(m.comment().mText); |
| 225 | printAnnotations(m.annotations()); |
| 226 | if (m.getTypeParameters() != null) { |
| 227 | printTypeVariableList(m.getTypeParameters()); |
| 228 | System.out.print(" "); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 229 | } |
| 230 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 231 | printTypeName(m.returnType()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 232 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 233 | System.out.print(" " + m.name() + m.flatSignature()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 234 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 235 | if (m.thrownExceptions() != null && !m.thrownExceptions().isEmpty()) { |
| 236 | System.out.print(" throws "); |
| 237 | for (ClassInfo c : m.thrownExceptions()) { |
| 238 | if (c != m.thrownExceptions().get(0)) { |
| 239 | System.out.print(", "); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 240 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 241 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 242 | System.out.print(c.name()); |
| 243 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | System.out.println(); |
| 247 | } |
| 248 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 249 | System.out.println(); |
| 250 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 251 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 252 | if (!cl.annotationElements().isEmpty()) { |
| 253 | System.out.println("\nAnnotation Elements:"); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 254 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 255 | for (MethodInfo m : cl.annotationElements()) { |
| 256 | if (m != cl.annotationElements().get(0)) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 257 | System.out.println(); |
| 258 | } |
| 259 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 260 | System.out.println(m.comment().mText); |
| 261 | printAnnotations(m.annotations()); |
| 262 | printTypeName(m.returnType()); |
| 263 | |
| 264 | System.out.print(" " + m.name() + m.flatSignature()); |
| 265 | |
| 266 | if (m.defaultAnnotationElementValue() != null) { |
| 267 | System.out.print(" default " + |
| 268 | m.defaultAnnotationElementValue().valueString()); |
| 269 | } |
| 270 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 271 | System.out.println(); |
| 272 | } |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 273 | |
| 274 | System.out.println(); |
| 275 | } |
| 276 | |
| 277 | if (cl.innerClasses() != null && !cl.innerClasses().isEmpty()) { |
| 278 | System.out.println("\nInner Classes:"); |
| 279 | for (ClassInfo c : cl.innerClasses()) { |
| 280 | printClassInfo(c); |
| 281 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 282 | } |
| 283 | } |
| 284 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 285 | private static void printTypeName(TypeInfo type) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 286 | System.out.print(type.simpleTypeName()); |
| 287 | |
| 288 | if (type.extendsBounds() != null && !type.extendsBounds().isEmpty()) { |
| 289 | System.out.print(" extends "); |
| 290 | for (TypeInfo t : type.extendsBounds()) { |
| 291 | if (t != type.extendsBounds().get(0)) { |
| 292 | System.out.print(" & "); |
| 293 | } |
| 294 | printTypeName(t); |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | if (type.superBounds() != null && !type.superBounds().isEmpty()) { |
| 299 | System.out.print(" super "); |
| 300 | for (TypeInfo t : type.superBounds()) { |
| 301 | if (t != type.superBounds().get(0)) { |
| 302 | System.out.print(" & "); |
| 303 | } |
| 304 | printTypeName(t); |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | printTypeVariables(type); |
| 309 | |
| 310 | if (type.dimension() != null) { |
| 311 | System.out.print(type.dimension()); |
| 312 | } |
| 313 | } |
| 314 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 315 | private static void printAnnotations(ArrayList<AnnotationInstanceInfo> annotations) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 316 | for (AnnotationInstanceInfo i : annotations) { |
| 317 | System.out.println(i); |
| 318 | } |
| 319 | } |
| 320 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 321 | private static void printTypeVariables(TypeInfo type) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 322 | printTypeVariableList(type.typeArguments()); |
| 323 | } |
| 324 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 325 | private static void printTypeVariableList(ArrayList<TypeInfo> typeList) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 326 | if (typeList != null && !typeList.isEmpty()) { |
| 327 | System.out.print("<"); |
| 328 | for (TypeInfo type : typeList) { |
| 329 | if (type != typeList.get(0)) { |
| 330 | System.out.print(", "); |
| 331 | } |
| 332 | printTypeName(type); |
| 333 | } |
| 334 | System.out.print(">"); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Parses the file represented by the ParseTree. |
| 340 | * @param tree A ParseTree of the file to parse. |
| 341 | */ |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 342 | private void parseFile(ParseTree tree) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 343 | if (tree.payload != null) { |
| 344 | String payload = tree.payload.toString(); |
| 345 | |
| 346 | // first pass at ignore method blocks |
| 347 | if ("block".equals(payload) || |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 348 | "blockStatement".equals(payload) || |
| 349 | "explicitConstructorInvocation".equals(payload)) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 350 | tree = null; |
| 351 | return; |
| 352 | } |
| 353 | |
| 354 | // parse package of file |
| 355 | if ("packageDeclaration".equals(payload)) { |
| 356 | mPackage = buildPackage(tree); |
| 357 | return; |
| 358 | // parse imports |
| 359 | } else if ("importDeclaration".equals(payload)) { |
| 360 | mImports.add(buildImport(tree)); |
| 361 | return; |
| 362 | // classes |
| 363 | } else if ("normalClassDeclaration".equals(payload)) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 364 | buildClass(tree, null); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 365 | return; |
| 366 | // enums |
| 367 | } else if ("enumDeclaration".equals(payload)) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 368 | buildEnum(tree, null); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 369 | return; |
| 370 | // interfaces |
| 371 | } else if ("normalInterfaceDeclaration".equals(payload)) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 372 | buildInterface(tree, null); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 373 | return; |
| 374 | // annotations |
| 375 | } else if ("annotationTypeDeclaration".equals(payload)) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 376 | buildAnnotationDeclaration(tree, null); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 377 | return; |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | // if we're not at the end, recurse down the tree |
| 382 | for (int i = 0; i < tree.getChildCount(); i++) { |
| 383 | parseFile((ParseTree) tree.getChild(i)); |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | /** |
| 388 | * Parses a packageDeclaration in the tree. This function should only be called once per file. |
| 389 | * @param tree The tree to parse. packageDeclaration should be the root value. |
| 390 | * @return a PackageInfo representing the package in which this file exists. |
| 391 | */ |
| 392 | private PackageInfo buildPackage(ParseTree tree) { |
| 393 | for (int i = 0; i < tree.getChildCount(); i++) { |
| 394 | ParseTree child = (ParseTree) tree.getChild(i); |
| 395 | |
| 396 | if (child.payload != null && "qualifiedName".equals(child.payload.toString())) { |
| 397 | String packageName = buildQualifiedName(child); |
| 398 | |
| 399 | // return package because we might be creating packages for other classes |
| 400 | return Caches.obtainPackage(packageName); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | return null; |
| 405 | } |
| 406 | |
| 407 | /** |
| 408 | * Parses a qualifiedName, returning it as a String. |
| 409 | * @param tree The tree to parse. qualifiedName should be the root value. |
| 410 | * @return |
| 411 | */ |
| 412 | private static String buildQualifiedName(ParseTree tree) { |
| 413 | StringBuilder packageName = new StringBuilder(); |
| 414 | |
| 415 | for (int j = 0; j < tree.getChildCount(); j++) { |
| 416 | packageName.append(tree.getChild(j).toString()); |
| 417 | } |
| 418 | |
| 419 | return packageName.toString(); |
| 420 | } |
| 421 | |
| 422 | /** |
| 423 | * Builds a string representing an import declaration. |
| 424 | * @param tree The tree to parse. importDeclaration should be the root value. |
| 425 | * @return a String version of the import. |
| 426 | */ |
| 427 | private String buildImport(ParseTree tree) { |
| 428 | StringBuilder theImport = new StringBuilder(); |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 429 | for (int i = 1; i < tree.getChildCount(); i++) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 430 | String part = tree.getChild(i).toString(); |
| 431 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 432 | if ((i == 1 && "static".equals(part)) |
| 433 | || (i == tree.getChildCount()-1 && ";".equals(part))) { |
| 434 | continue; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 435 | } |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 436 | |
| 437 | theImport.append(part); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 438 | } |
| 439 | |
| 440 | return theImport.toString(); |
| 441 | } |
| 442 | |
| 443 | /** |
| 444 | * Builds a ClassInfo for a normalClassDeclaration. |
| 445 | * @param tree The tree to parse. normalClassDeclaration should be the root value. |
| 446 | * @param containingClass The class that contains the class that will be built. |
| 447 | * This value should be null if this class is a root class in the file. |
| 448 | * @return A ClassInfo that contains all of the information about the class. |
| 449 | */ |
| 450 | private ClassInfo buildClass(ParseTree tree, ClassInfo containingClass) { |
| 451 | CommentAndPosition commentAndPosition = parseCommentAndPosition(tree); |
| 452 | Modifiers modifiers = new Modifiers(this); |
| 453 | ClassInfo cls = null; |
| 454 | |
| 455 | @SuppressWarnings("unchecked") |
| 456 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 457 | ParseTree child = it.next(); |
| 458 | |
| 459 | // parse modifiers |
| 460 | modifiers.parseModifiers(child); |
| 461 | |
| 462 | it.next(); |
| 463 | child = it.next(); |
| 464 | |
| 465 | // parse class name |
| 466 | cls = buildClassName(child, containingClass, modifiers, |
| 467 | commentAndPosition.getCommentText(), |
| 468 | commentAndPosition.getPosition(), |
| 469 | ClassType.ORDINARY); |
| 470 | |
| 471 | child = it.next(); |
| 472 | |
| 473 | // handle generics |
| 474 | if ("typeParameters".equals(child.toString())) { |
| 475 | cls.type().setTypeArguments(buildTypeVariables(child)); |
| 476 | child = it.next(); |
| 477 | |
| 478 | } |
| 479 | |
| 480 | // handle extends |
| 481 | if ("extends".equals(child.toString())) { |
| 482 | child = it.next(); |
| 483 | |
| 484 | TypeInfo type = buildType(child); |
| 485 | cls.setSuperclassType(type); |
| 486 | |
| 487 | // if ClassInfo is null, we need to add a resolution |
| 488 | if (type.asClassInfo() == null) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 489 | addFutureResolution(cls, "superclassQualifiedName", type.simpleTypeName(), this); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 490 | } |
| 491 | |
| 492 | cls.setSuperClass(type.asClassInfo()); |
| 493 | |
| 494 | child = it.next(); |
| 495 | } |
| 496 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 497 | // TODO - do I have to make java.lang.Object the superclass if there is none otherwise? |
| 498 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 499 | // handle implements |
| 500 | if ("implements".equals(child.toString())) { |
| 501 | child = it.next(); |
| 502 | |
| 503 | parseInterfaces(child, cls); |
| 504 | |
| 505 | child = it.next(); |
| 506 | } |
| 507 | |
| 508 | // finally, parse the body |
| 509 | buildClassBody(child, cls); |
| 510 | |
| 511 | return cls; |
| 512 | } |
| 513 | |
| 514 | /** |
| 515 | * Parses the list of interfaces that the class implements. |
| 516 | * Should only be called if the implements keyword is found. |
| 517 | * @param tree The tree to parse. typeList should be the root element. |
| 518 | * @param cls The class that implements these interfaces. |
| 519 | */ |
| 520 | private void parseInterfaces(ParseTree tree, ClassInfo cls) { |
| 521 | for (Object o : tree.getChildren()) { |
| 522 | if ("type".equals(o.toString())) { |
| 523 | TypeInfo type = buildType((ParseTree) o); |
| 524 | cls.addInterfaceType(type); |
| 525 | |
| 526 | // if ClassInfo is null, we need to add a resolution |
| 527 | if (type.asClassInfo() == null) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 528 | addFutureResolution(cls, "interfaceQualifiedName", type.simpleTypeName(), this); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 529 | } |
| 530 | |
| 531 | cls.addInterface(type.asClassInfo()); |
| 532 | } |
| 533 | } |
| 534 | } |
| 535 | |
| 536 | /** |
| 537 | * ClassType exists solely to tell buildClassName which type of ClassInfo is being built. |
| 538 | */ |
| 539 | private enum ClassType { |
| 540 | ENUM, INTERFACE, ANNOTATION, ORDINARY |
| 541 | } |
| 542 | |
| 543 | /** |
| 544 | * Parses the class name from the declaration. Also initializes the class. |
| 545 | * @param tree Position of the tree where the name of the class resides. |
| 546 | * @param containingClass Class that this class is contained within. |
| 547 | * <tt>null</tt> if this class is the root class. |
| 548 | * @param modifiers Contains all the modifiers of this class. |
| 549 | * @param commentText Javadoc comment of this class. |
| 550 | * @param position Position of the class. |
| 551 | * @param classType Type of class being instantiated. |
| 552 | * @return the ClassInfo being initialized. |
| 553 | */ |
| 554 | private ClassInfo buildClassName(ParseTree tree, ClassInfo containingClass, Modifiers modifiers, |
| 555 | String commentText, SourcePositionInfo position, ClassType classType) { |
| 556 | String qualifiedClassName = null; |
| 557 | boolean isOrdinaryClass = true; |
| 558 | boolean isException = false; |
| 559 | boolean isError = false; |
| 560 | boolean isIncluded = false; |
| 561 | boolean isPrimitive = false; |
| 562 | boolean isEnum = false; |
| 563 | boolean isInterface = false; |
| 564 | boolean isAnnotation = false; |
| 565 | |
| 566 | // set appropriate flags based on ClassType |
| 567 | switch (classType) { |
| 568 | case ENUM: |
| 569 | isEnum = true; |
| 570 | break; |
| 571 | case INTERFACE: |
| 572 | isInterface = true; |
| 573 | break; |
| 574 | case ANNOTATION: |
| 575 | isAnnotation = true; |
| 576 | break; |
| 577 | } |
| 578 | |
| 579 | String qualifiedTypeName = null; |
| 580 | ClassInfo cls = null; |
| 581 | |
| 582 | // changes the name based upon whether this is the root class or an inner class |
| 583 | if (containingClass == null) { |
| 584 | qualifiedClassName = mPackage.name() + "." + tree.toString(); |
| 585 | } else { |
| 586 | qualifiedClassName = containingClass.qualifiedName() + "." + tree.toString(); |
| 587 | } |
| 588 | |
| 589 | qualifiedTypeName = new String(qualifiedClassName); |
| 590 | |
| 591 | // add the name to mClassNames so that we can use it to resolve usages of this class |
| 592 | mClassNames.add(qualifiedClassName); |
| 593 | |
| 594 | // get the class from the cache and initialize it |
| 595 | cls = Caches.obtainClass(qualifiedClassName); |
| 596 | cls.initialize(commentText, position, |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 597 | modifiers.isPublic(), modifiers.isProtected(), |
| 598 | modifiers.isPackagePrivate(), modifiers.isPrivate(), |
| 599 | modifiers.isStatic(), isInterface, modifiers.isAbstract(), |
| 600 | isOrdinaryClass, isException, isError, isEnum, isAnnotation, |
| 601 | modifiers.isFinal(), isIncluded, qualifiedTypeName, isPrimitive, |
| 602 | modifiers.getAnnotations()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 603 | |
| 604 | cls.setContainingClass(containingClass); |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 605 | cls.setContainingPackage(mPackage); |
| 606 | |
| 607 | if (containingClass == null) { |
| 608 | mRootClass = cls; |
| 609 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 610 | |
| 611 | // create an set a TypeInfo for this class |
| 612 | TypeInfo type = new TypeInfo(false, null, cls.name(), qualifiedTypeName, cls); |
| 613 | cls.setTypeInfo(type); |
| 614 | |
| 615 | return cls; |
| 616 | } |
| 617 | |
| 618 | /** |
| 619 | * Parses the body of a class. |
| 620 | * @param tree The tree to parse. classBody should be the root value. |
| 621 | * @param cls |
| 622 | */ |
| 623 | private void buildClassBody(ParseTree tree, ClassInfo cls) { |
| 624 | for (Object o : tree.getChildren()) { |
| 625 | ParseTree child = (ParseTree) o; |
| 626 | |
| 627 | // skip all of the cruft that isn't a declaration |
| 628 | if (!"classBodyDeclaration".equals(child.toString())) { |
| 629 | continue; |
| 630 | } |
| 631 | |
| 632 | // get to an actual definition |
| 633 | ParseTree member = (ParseTree) child.getChild(0).getChild(0); |
| 634 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 635 | // ignores static initializers |
| 636 | if (member == null) { |
| 637 | continue; |
| 638 | } |
| 639 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 640 | // field |
| 641 | if ("fieldDeclaration".equals(member.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 642 | for (FieldInfo f : buildFields(member, cls)) { |
| 643 | cls.addField(f); |
| 644 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 645 | // method and constructor |
| 646 | } else if ("methodDeclaration".equals(member.toString())) { |
| 647 | MethodInfo method = buildMethod(member, cls, false); |
| 648 | |
| 649 | if (method.kind().equals("constructor")) { |
| 650 | cls.addConstructor(method); |
| 651 | } else { |
| 652 | cls.addMethod(method); |
| 653 | } |
| 654 | // classes and enums |
| 655 | } else if ("classDeclaration".equals(member.toString())) { |
| 656 | Object tmp = member.getChild(0); |
| 657 | |
| 658 | if ("normalClassDeclaration".equals(tmp.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 659 | cls.addInnerClass(buildClass((ParseTree) tmp, cls)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 660 | } else if ("enumDeclaration".equals(tmp.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 661 | cls.addInnerClass(buildEnum((ParseTree) tmp, cls)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 662 | } |
| 663 | // interfaces and annotations |
| 664 | } else if ("interfaceDeclaration".equals(member.toString())) { |
| 665 | Object tmp = member.getChild(0); |
| 666 | |
| 667 | if ("normalInterfaceDeclaration".equals(tmp.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 668 | cls.addInnerClass(buildInterface((ParseTree) tmp, cls)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 669 | } else if ("annotationTypeDeclaration".equals(tmp.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 670 | cls.addInnerClass(buildAnnotationDeclaration((ParseTree) tmp, cls)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 671 | } |
| 672 | } |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | /** |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 677 | * Builds one or more FieldInfos for the field declared in this class. |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 678 | * @param tree The tree to parse. fieldDeclaration should be the root value. |
| 679 | * @param containingClass The ClassInfo in which this field is contained. |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 680 | * @return A list of FieldInfos for this field declaration. |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 681 | */ |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 682 | private ArrayList<FieldInfo> buildFields(ParseTree tree, ClassInfo containingClass) { |
| 683 | ArrayList<FieldInfo> fields = new ArrayList<FieldInfo>(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 684 | Modifiers modifiers = new Modifiers(this); |
| 685 | CommentAndPosition commentAndPosition = parseCommentAndPosition(tree); |
| 686 | String name = null; |
| 687 | Object constantValue = null; |
| 688 | TypeInfo type = null; |
| 689 | boolean hasValue = false; |
| 690 | |
| 691 | @SuppressWarnings("unchecked") |
| 692 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 693 | ParseTree child = it.next(); |
| 694 | |
| 695 | // modifiers |
| 696 | modifiers.parseModifiers(child); |
| 697 | child = it.next(); |
| 698 | |
| 699 | // parse the type of this field |
| 700 | type = buildType(child); |
| 701 | |
| 702 | child = it.next(); |
| 703 | |
| 704 | // parse the variable declarators |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 705 | boolean firstType = true; |
| 706 | while (!";".equals(child.toString())) { |
| 707 | if ("variableDeclarator".equals(child.toString())) { |
| 708 | TypeInfo newType; |
| 709 | if (firstType) { |
| 710 | firstType = false; |
| 711 | newType = type; |
| 712 | } else { |
| 713 | newType = new TypeInfo(type.isPrimitive(), type.dimension(), |
| 714 | type.simpleTypeName(), type.qualifiedTypeName(), type.asClassInfo()); |
| 715 | newType.setBounds(type.superBounds(), type.extendsBounds()); |
| 716 | newType.setIsWildcard(type.isWildcard()); |
| 717 | newType.setIsTypeVariable(type.isTypeVariable()); |
| 718 | newType.setTypeArguments(type.typeArguments()); |
| 719 | } |
| 720 | name = child.getChild(0).toString(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 721 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 722 | // if we have a value for the field and/or dimensions |
| 723 | if (child.getChildCount() > 1) { |
| 724 | int j = 1; |
| 725 | ParseTree tmp = (ParseTree) child.getChild(j++); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 726 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 727 | // if we have dimensions in the wrong place |
| 728 | if ("[".equals(tmp.toString())) { |
| 729 | StringBuilder builder = new StringBuilder(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 730 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 731 | do { |
| 732 | builder.append(tmp.toString()); |
| 733 | tmp = (ParseTree) child.getChild(j++); |
| 734 | } while (j < child.getChildCount() && !"=".equals(tmp.toString())); |
| 735 | |
| 736 | newType.setDimension(builder.toString()); |
| 737 | } |
| 738 | |
| 739 | // get value if it exists |
| 740 | if (j < child.getChildCount()) { |
| 741 | // get to variableInitializer |
| 742 | do { |
| 743 | tmp = (ParseTree) child.getChild(j++); |
| 744 | } while (!"variableInitializer".equals(tmp.toString())); |
| 745 | |
| 746 | // get the constantValue |
| 747 | constantValue = parseExpression(tmp); |
| 748 | } |
| 749 | |
| 750 | hasValue = true; |
| 751 | } |
| 752 | |
| 753 | FieldInfo field = new FieldInfo(name, containingClass, containingClass, |
| 754 | modifiers.isPublic(), modifiers.isProtected(), |
| 755 | modifiers.isPackagePrivate(), modifiers.isPrivate(), |
| 756 | modifiers.isFinal(), modifiers.isStatic(), modifiers.isTransient(), |
| 757 | modifiers.isVolatile(), modifiers.isSynthetic(), |
| 758 | newType, commentAndPosition.getCommentText(), constantValue, |
| 759 | commentAndPosition.getPosition(), modifiers.getAnnotations()); |
| 760 | field.setHasValue(hasValue); |
| 761 | fields.add(field); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 762 | } |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 763 | |
| 764 | child = it.next(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 765 | } |
| 766 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 767 | return fields; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 768 | } |
| 769 | |
| 770 | /** |
| 771 | * Parses an expression in the ParseTree to get a constant value. |
| 772 | * @param tree the place in the tree to get the constant value. |
| 773 | * @return the constant value. |
| 774 | */ |
| 775 | private static Object parseExpression(ParseTree tree) { |
| 776 | Object constantValue = null; |
| 777 | StringBuilder builder = new StringBuilder(); |
| 778 | |
| 779 | while (!"primary".equals(tree.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 780 | if (tree.getChildCount() > 1) { |
| 781 | if ("unaryExpression".equals(tree.toString()) || |
| 782 | "unaryExpressionNotPlusMinus".equals(tree.toString())) { |
| 783 | if ("selector".equals(tree.getChild(1).toString())) { |
| 784 | return constantValue; |
| 785 | } |
| 786 | |
| 787 | builder.append(tree.getChild(0)); |
| 788 | tree = (ParseTree) tree.getChild(1); |
| 789 | } else if ("arrayInitializer".equals(tree.toString())) { |
| 790 | // TODO - do we wanna parse arrays or just skip it |
| 791 | return constantValue; |
| 792 | } else { |
| 793 | return constantValue; |
| 794 | } |
| 795 | } else if ("castExpression".equals(tree.toString())) { |
| 796 | tree = (ParseTree) tree.getChild(tree.getChildCount()-1); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 797 | } else { |
| 798 | tree = (ParseTree) tree.getChild(0); |
| 799 | } |
| 800 | } |
| 801 | |
| 802 | if ("literal".equals(tree.getChild(0).toString())) { |
| 803 | constantValue = builder.append(tree.getChild(0).getChild(0).toString()).toString(); |
| 804 | } else if (tree.getChildCount() > 1) { |
| 805 | for (Object o : tree.getChildren()) { |
| 806 | builder.append(o.toString()); |
| 807 | } |
| 808 | |
| 809 | constantValue = builder.toString(); |
| 810 | } |
| 811 | |
| 812 | return constantValue; |
| 813 | } |
| 814 | |
| 815 | /** |
| 816 | * Builds TypeInfo. Requires that tree points to "type" in the ParseTree. |
| 817 | * @param tree The tree to parse. type should be the root value. |
| 818 | * @return A TypeInfo for this type. |
| 819 | */ |
| 820 | private TypeInfo buildType(ParseTree tree) { |
| 821 | boolean isPrimitive = false; |
| 822 | String dimension = null; |
| 823 | String simpleTypeName = null; |
| 824 | String qualifiedTypeName = null; |
| 825 | ClassInfo cl = null; |
| 826 | boolean addResolution = false; |
| 827 | ArrayList<TypeInfo> typeArguments = null; |
| 828 | |
| 829 | // parse primitive types - very easy |
| 830 | if ("primitiveType".equals(tree.getChild(0).toString())) { |
| 831 | isPrimitive = true; |
| 832 | |
| 833 | simpleTypeName = tree.getChild(0).getChild(0).toString(); |
| 834 | qualifiedTypeName = simpleTypeName; |
| 835 | // any non-primitives |
| 836 | } else { |
| 837 | StringBuilder builder = new StringBuilder(); |
| 838 | |
| 839 | // get the full name of the type |
| 840 | for (Object namePart : ((ParseTree) tree.getChild(0)).getChildren()) { |
| 841 | // if we get to typeArguments, aka generics, parse that and bale out |
| 842 | // of building the name |
| 843 | if ("typeArguments".equals(namePart.toString())) { |
| 844 | typeArguments = buildTypeVariables((ParseTree) namePart); |
| 845 | break; |
| 846 | } |
| 847 | |
| 848 | builder.append(namePart.toString()); |
| 849 | } |
| 850 | |
| 851 | // get simple and qualified name |
| 852 | simpleTypeName = builder.toString(); |
| 853 | StringBuilder qualifiedTypeNameBuilder = new StringBuilder(); |
| 854 | boolean isGeneric = resolveQualifiedName(simpleTypeName, |
| 855 | qualifiedTypeNameBuilder, this); |
| 856 | qualifiedTypeName = qualifiedTypeNameBuilder.toString(); |
| 857 | |
| 858 | // if we couldn't figure out the qualified name |
| 859 | // tell us we need to resolve this |
| 860 | // can't add the resolution until the TypeInfo has been created |
| 861 | if ("".equals(qualifiedTypeName)) { |
| 862 | addResolution = true; |
| 863 | // otherwise, if the name is not a generic, get the class that this Type refers to |
| 864 | } else if (!isGeneric) { |
| 865 | cl = Caches.obtainClass(qualifiedTypeName); |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | // get the dimensions of this type |
| 870 | dimension = getDimensions(tree); |
| 871 | |
| 872 | TypeInfo type = new TypeInfo(isPrimitive, dimension, simpleTypeName, qualifiedTypeName, cl); |
| 873 | type.setTypeArguments(typeArguments); |
| 874 | |
| 875 | if (addResolution) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 876 | addFutureResolution(type, "class", simpleTypeName, this); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 877 | } |
| 878 | |
| 879 | return type; |
| 880 | } |
| 881 | |
| 882 | /** |
| 883 | * Processes the type variables of a class that contains generics. |
| 884 | * @param tree Root of the type parameters. |
| 885 | * @param cls Class in which these type variables are contained. |
| 886 | */ |
| 887 | private ArrayList<TypeInfo> buildTypeVariables(ParseTree tree) { |
| 888 | ArrayList<TypeInfo> typeVariables = new ArrayList<TypeInfo>(); |
| 889 | ArrayList<TypeInfo> superBounds = new ArrayList<TypeInfo>(); |
| 890 | ArrayList<TypeInfo> extendsBounds = new ArrayList<TypeInfo>(); |
| 891 | |
| 892 | for (Object o : tree.getChildren()) { |
| 893 | // if we're not dealing with a type, skip |
| 894 | // basically gets rid of commas and lessthan and greater than signs |
| 895 | if (!o.toString().equals("typeParameter") && |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 896 | !o.toString().equals("typeArgument")) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 897 | continue; |
| 898 | } |
| 899 | |
| 900 | ParseTree typeParameter = (ParseTree) o; |
| 901 | |
| 902 | TypeInfo type; |
| 903 | // if we have a typeArgument and it is not a wildcard |
| 904 | if ("typeArgument".equals(typeParameter.toString()) && |
| 905 | !"?".equals(typeParameter.getChild(0).toString())) { |
| 906 | type = buildType((ParseTree) typeParameter.getChild(0)); |
| 907 | } else { |
| 908 | // otherwise, we have a wildcard or parameter |
| 909 | // which can be more vague because of generics |
| 910 | String name = typeParameter.getChild(0).toString(); |
| 911 | |
| 912 | type = new TypeInfo(false, null, name, name, null); |
| 913 | if ("?".equals(name)) { |
| 914 | type.setIsWildcard(true); |
| 915 | } else { |
| 916 | // add generic |
| 917 | mClassNames.add(name); |
| 918 | } |
| 919 | } |
| 920 | |
| 921 | // if we have an extends or super on our type variable |
| 922 | if (typeParameter.getChildCount() > 1) { |
| 923 | ParseTree value = (ParseTree) typeParameter.getChild(1); |
| 924 | |
| 925 | if ("extends".equals(value.toString())) { |
| 926 | value = (ParseTree) typeParameter.getChild(2); |
| 927 | |
| 928 | // wildcard extends |
| 929 | if ("type".equals(value.toString())) { |
| 930 | extendsBounds.add(buildType(value)); |
| 931 | // all other extends |
| 932 | } else { |
| 933 | // will have to handle stuff with typeBound - multiple types |
| 934 | for (Object obj : value.getChildren()) { |
| 935 | if ("type".equals(obj.toString())) { |
| 936 | extendsBounds.add(buildType((ParseTree) obj)); |
| 937 | } |
| 938 | } |
| 939 | } |
| 940 | } else if ("super".equals(value.toString())) { |
| 941 | superBounds.add(buildType((ParseTree) typeParameter.getChild(2))); |
| 942 | } |
| 943 | } |
| 944 | |
| 945 | type.setIsTypeVariable(true); |
| 946 | type.setBounds(superBounds, extendsBounds); |
| 947 | typeVariables.add(type); |
| 948 | } |
| 949 | |
| 950 | return typeVariables; |
| 951 | } |
| 952 | |
| 953 | /** |
| 954 | * Builds a MethodInfo for methods, constructors and annotation elements. |
| 955 | * @param tree The tree to parse. methodDeclaration, interfaceMethodDeclaration |
| 956 | * or annotationMethodDeclaration should be the root value. |
| 957 | * @param containingClass the class in which this method exists. |
| 958 | * @param isAnnotation true if the class is an annotation element |
| 959 | * @return the MethodInfo |
| 960 | */ |
| 961 | private MethodInfo buildMethod(ParseTree tree, ClassInfo containingClass, |
| 962 | boolean isAnnotation) { |
| 963 | Modifiers modifiers = new Modifiers(this); |
| 964 | CommentAndPosition commentAndPosition = parseCommentAndPosition(tree); |
| 965 | |
| 966 | String name = null; |
| 967 | StringBuilder flatSignature = new StringBuilder().append('('); |
| 968 | ArrayList<TypeInfo> typeParameters = null; |
| 969 | ArrayList<ParameterInfo> parameters = new ArrayList<ParameterInfo>(); |
| 970 | ArrayList<ClassInfo> thrownExceptions = new ArrayList<ClassInfo>(); |
| 971 | TypeInfo returnType = null; |
| 972 | boolean isAnnotationElement = false; |
| 973 | boolean isVarArg = false; |
| 974 | String kind = "method"; // annotationElement, method, or constructor |
| 975 | AnnotationValueInfo elementValue = null; |
| 976 | ArrayList<Resolution> pendingResolutions = new ArrayList<Resolution>(); |
| 977 | |
| 978 | @SuppressWarnings("unchecked") |
| 979 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 980 | ParseTree child = it.next(); |
| 981 | |
| 982 | modifiers.parseModifiers(child); |
| 983 | |
| 984 | child = it.next(); |
| 985 | |
| 986 | // generics stuff |
| 987 | if ("typeParameters".equals(child.toString())) { |
| 988 | typeParameters = buildTypeVariables(child); |
| 989 | child = it.next(); |
| 990 | } |
| 991 | |
| 992 | // handle returnType if we're not in a constructor |
| 993 | if ("type".equals(child.toString())) { |
| 994 | returnType = buildType(child); |
| 995 | child = it.next(); |
| 996 | } else if ("void".equals(child.toString())) { |
| 997 | returnType = new TypeInfo(true, null, "void", "void", null); |
| 998 | child = it.next(); |
| 999 | } |
| 1000 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1001 | // this is the method name |
| 1002 | name = child.toString(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1003 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1004 | if (name.equals(containingClass.name())) { |
| 1005 | kind = "constructor"; |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1006 | } |
| 1007 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1008 | // probably don't need this check any longer since I unrolled the loop |
| 1009 | // if (isConstructorOrMethodName(child)) { |
| 1010 | // // this is the method name |
| 1011 | // name = child.toString(); |
| 1012 | // |
| 1013 | // if (name.equals(containingClass.name())) { |
| 1014 | // kind = "constructor"; |
| 1015 | // } |
| 1016 | // } |
| 1017 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1018 | child = it.next(); |
| 1019 | |
| 1020 | // method parameters |
| 1021 | if ("formalParameters".equals(child.toString())) { |
| 1022 | isVarArg = buildMethodParameters(child, parameters, flatSignature); |
| 1023 | } else { |
| 1024 | child = it.next(); |
| 1025 | } |
| 1026 | |
| 1027 | child = it.next(); |
| 1028 | flatSignature.append(')'); |
| 1029 | |
| 1030 | // handle exception throwing |
| 1031 | if ("throws".equals(child.toString())) { |
| 1032 | child = it.next(); |
| 1033 | |
| 1034 | for (Object o : child.getChildren()) { |
| 1035 | if (",".equals(o.toString())) { |
| 1036 | continue; |
| 1037 | } |
| 1038 | |
| 1039 | // get the name of the exception, resolve it and add it to the list |
| 1040 | // unless we can't, in which case, add a resolution |
| 1041 | String exceptionName = buildQualifiedName(((ParseTree) o)); |
| 1042 | StringBuilder exceptionQualifiedName = new StringBuilder(); |
| 1043 | boolean isGeneric = resolveQualifiedName(exceptionName, |
| 1044 | exceptionQualifiedName, this); |
| 1045 | |
| 1046 | if ("".equals(exceptionQualifiedName.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1047 | pendingResolutions.add(new Resolution("thrownException", exceptionName, null)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1048 | } else if (!isGeneric) { |
| 1049 | thrownExceptions.add(Caches.obtainClass(exceptionQualifiedName.toString())); |
| 1050 | } |
| 1051 | } |
| 1052 | // handle default values for annotation elements |
| 1053 | } else if ("default".equals(child.toString())) { |
| 1054 | child = it.next(); |
| 1055 | |
| 1056 | elementValue = buildElementValue(child, this); |
| 1057 | child = it.next(); |
| 1058 | } |
| 1059 | |
| 1060 | if (isAnnotation) { |
| 1061 | kind = "annotationElement"; |
| 1062 | } |
| 1063 | |
| 1064 | // Here we set signature, overridden method to null because |
| 1065 | // MethodInfo figures these values out later on |
| 1066 | MethodInfo method = new MethodInfo(commentAndPosition.getCommentText(), typeParameters, |
| 1067 | name, null, containingClass, containingClass, modifiers.isPublic(), |
| 1068 | modifiers.isProtected(), modifiers.isPackagePrivate(), |
| 1069 | modifiers.isPrivate(), modifiers.isFinal(), |
| 1070 | modifiers.isStatic(), modifiers.isSynthetic(), |
| 1071 | modifiers.isAbstract(), modifiers.isSynchronized(), |
| 1072 | false, isAnnotationElement, kind, flatSignature.toString(), |
| 1073 | null, returnType, parameters, thrownExceptions, |
| 1074 | commentAndPosition.getPosition(), modifiers.getAnnotations()); |
| 1075 | |
| 1076 | method.setVarargs(isVarArg); |
| 1077 | method.init(elementValue); |
| 1078 | |
| 1079 | for (Resolution r : pendingResolutions) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1080 | addFutureResolution(method, r.getVariable(), r.getValue(), this); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1081 | } |
| 1082 | |
| 1083 | return method; |
| 1084 | } |
| 1085 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1086 | /** |
| 1087 | * Build the method parameters. |
| 1088 | * @param tree The tree to parse. formalParamaters should be the root value. |
| 1089 | * @param parameters List to put the method ParamaterInfos into. |
| 1090 | * @param flatSignature Pass in a StringBuilder with "(" in it to build the |
| 1091 | * flatSignature of the MethodInfo |
| 1092 | * @return true if the Method has a VarArgs parameter. false otherwise. |
| 1093 | */ |
| 1094 | private boolean buildMethodParameters(ParseTree tree, |
| 1095 | ArrayList<ParameterInfo> parameters, |
| 1096 | StringBuilder flatSignature) { |
| 1097 | boolean isVarArg = false; |
| 1098 | for (Object obj : tree.getChildren()) { |
| 1099 | ParseTree child = (ParseTree) obj; |
| 1100 | |
| 1101 | if ("formalParameterDecls".equals(child.toString())) { |
| 1102 | for (Object formalParam : child.getChildren()) { |
| 1103 | ParseTree param = (ParseTree) formalParam; |
| 1104 | TypeInfo type = null; |
| 1105 | |
| 1106 | if (param.getChildCount() == 0) { |
| 1107 | continue; |
| 1108 | } |
| 1109 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1110 | @SuppressWarnings("unchecked") |
| 1111 | Iterator<ParseTree> it = (Iterator<ParseTree>) param.getChildren().iterator(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1112 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1113 | ParseTree paramPart = it.next(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1114 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1115 | if ("variableModifiers".equals(paramPart.toString())) { |
| 1116 | // TODO - handle variable modifiers - final, etc |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1117 | } |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1118 | |
| 1119 | paramPart = it.next(); |
| 1120 | |
| 1121 | type = buildType(paramPart); |
| 1122 | |
| 1123 | buildSignatureForType(flatSignature, type); |
| 1124 | |
| 1125 | if (param != child.getChildren().get(child.getChildCount()-1)) { |
| 1126 | flatSignature.append(", "); |
| 1127 | } |
| 1128 | |
| 1129 | paramPart = it.next(); |
| 1130 | |
| 1131 | if ("...".equals(paramPart.toString())) { |
| 1132 | isVarArg = true; |
| 1133 | // thank you varargs for only being the last parameter |
| 1134 | // you make life so much nicer |
| 1135 | flatSignature.append("..."); |
| 1136 | paramPart = it.next(); |
| 1137 | } |
| 1138 | |
| 1139 | String name = paramPart.toString(); |
| 1140 | |
| 1141 | CommentAndPosition commentAndPosition = new CommentAndPosition(); |
| 1142 | commentAndPosition.setPosition(paramPart); |
| 1143 | |
| 1144 | parameters.add(new ParameterInfo(name, type.qualifiedTypeName(), type, |
| 1145 | isVarArg, commentAndPosition.getPosition())); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1146 | } |
| 1147 | } |
| 1148 | } |
| 1149 | |
| 1150 | return isVarArg; |
| 1151 | } |
| 1152 | |
| 1153 | /** |
| 1154 | * Builds a StringBuilder representing the Type, including type arguments. |
| 1155 | * @param builder StringBuilder in which the Type will be placed. |
| 1156 | * @param type the TypeInfo to turn into a String. |
| 1157 | */ |
| 1158 | private void buildSignatureForType(StringBuilder builder, TypeInfo type) { |
| 1159 | // simple name |
| 1160 | builder.append(type.simpleTypeName()); |
| 1161 | |
| 1162 | // generics |
| 1163 | if (type.typeArguments() != null && !type.typeArguments().isEmpty()) { |
| 1164 | builder.append('<'); |
| 1165 | for (TypeInfo inner : type.typeArguments()) { |
| 1166 | if (inner != type.typeArguments().get(0)) { |
| 1167 | builder.append(", "); |
| 1168 | } |
| 1169 | |
| 1170 | // recurse |
| 1171 | buildSignatureForType(builder, inner); |
| 1172 | } |
| 1173 | builder.append('>'); |
| 1174 | } |
| 1175 | } |
| 1176 | |
| 1177 | /** |
| 1178 | * Builds a ClassInfo for an enum. |
| 1179 | * @param tree The tree to parse. enumDeclaration should be the root value. |
| 1180 | * @param containingClass ClassInfo that contains the enum declaration. |
| 1181 | * null if the enum is a root class. |
| 1182 | * @return the enum as a ClassInfo |
| 1183 | */ |
| 1184 | private ClassInfo buildEnum(ParseTree tree, ClassInfo containingClass) { |
| 1185 | CommentAndPosition commentAndPosition = parseCommentAndPosition(tree); |
| 1186 | Modifiers modifiers = new Modifiers(this); |
| 1187 | ClassInfo cls = null; |
| 1188 | |
| 1189 | @SuppressWarnings("unchecked") |
| 1190 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 1191 | |
| 1192 | ParseTree child = it.next(); |
| 1193 | |
| 1194 | modifiers.parseModifiers(child); |
| 1195 | |
| 1196 | child = it.next(); |
| 1197 | child = it.next(); |
| 1198 | |
| 1199 | cls = buildClassName(child, containingClass, modifiers, |
| 1200 | commentAndPosition.getCommentText(), |
| 1201 | commentAndPosition.getPosition(), ClassType.ENUM); |
| 1202 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1203 | child = it.next(); |
| 1204 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1205 | // handle implements |
| 1206 | if ("implements".equals(child.toString())) { |
| 1207 | child = it.next(); |
| 1208 | |
| 1209 | parseInterfaces(child, cls); |
| 1210 | |
| 1211 | child = it.next(); |
| 1212 | } |
| 1213 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1214 | buildEnumBody(child, cls); |
| 1215 | |
| 1216 | return cls; |
| 1217 | } |
| 1218 | |
| 1219 | /** |
| 1220 | * Parses the body of an enum. |
| 1221 | * @param tree The tree to parse. enumBody should be the root value. |
| 1222 | * @param containingClass ClassInfo to which this enum body pertains. |
| 1223 | */ |
| 1224 | private void buildEnumBody(ParseTree tree, ClassInfo containingClass) { |
| 1225 | for (Object o : tree.getChildren()) { |
| 1226 | ParseTree child = (ParseTree) o; |
| 1227 | |
| 1228 | if ("enumConstants".equals(child.toString())) { |
| 1229 | for (Object o2 : child.getChildren()) { |
| 1230 | ParseTree tmp = (ParseTree) o2; |
| 1231 | |
| 1232 | if ("enumConstant".equals(tmp.toString())) { |
| 1233 | containingClass.addEnumConstant(buildEnumConstant(tmp, containingClass)); |
| 1234 | } |
| 1235 | } |
| 1236 | } else if ("enumBodyDeclarations".equals(child.toString())) { |
| 1237 | buildClassBody(child, containingClass); |
| 1238 | } |
| 1239 | } |
| 1240 | return; |
| 1241 | } |
| 1242 | |
| 1243 | /** |
| 1244 | * Builds an enum constant. |
| 1245 | * @param tree The tree to parse. enumConstant should be the root value. |
| 1246 | * @param containingClass ClassInfo to which this enum constant pertains. |
| 1247 | * @return |
| 1248 | */ |
| 1249 | private FieldInfo buildEnumConstant(ParseTree tree, ClassInfo containingClass) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1250 | @SuppressWarnings("unchecked") |
| 1251 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 1252 | ParseTree child = it.next(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1253 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1254 | Modifiers modifiers = new Modifiers(this); |
| 1255 | if ("annotations".equals(child.toString())) { |
| 1256 | modifiers.parseModifiers(child); |
| 1257 | child = it.next(); |
| 1258 | } |
| 1259 | |
| 1260 | String name = child.toString(); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1261 | CommentAndPosition commentAndPosition = new CommentAndPosition(); |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1262 | commentAndPosition.setCommentText(child); |
| 1263 | commentAndPosition.setPosition(child); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1264 | Object constantValue = null; |
| 1265 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1266 | // get constantValue if it exists |
| 1267 | if (it.hasNext()) { |
| 1268 | child = it.next(); |
| 1269 | |
| 1270 | // if we have an expressionList |
| 1271 | if (child.getChildCount() == 3) { |
| 1272 | StringBuilder builder = new StringBuilder(); |
| 1273 | child = (ParseTree) child.getChild(1); // get the middle child |
| 1274 | |
| 1275 | for (Object o : child.getChildren()) { |
| 1276 | if ("expression".equals(o.toString())) { |
| 1277 | builder.append(parseExpression((ParseTree) o)); |
| 1278 | |
| 1279 | if (o != child.getChild(child.getChildCount()-1)) { |
| 1280 | builder.append(", "); |
| 1281 | } |
| 1282 | } |
| 1283 | } |
| 1284 | |
| 1285 | constantValue = builder.toString(); |
| 1286 | } |
| 1287 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1288 | |
| 1289 | return new FieldInfo(name, containingClass, containingClass, containingClass.isPublic(), |
| 1290 | containingClass.isProtected(), containingClass.isPackagePrivate(), |
| 1291 | containingClass.isPrivate(), containingClass.isFinal(), |
| 1292 | containingClass.isStatic(), false, false, false, |
| 1293 | containingClass.type(), commentAndPosition.getCommentText(), |
| 1294 | constantValue, commentAndPosition.getPosition(), |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1295 | modifiers.getAnnotations()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1296 | } |
| 1297 | |
| 1298 | /** |
| 1299 | * Builds a ClassInfo for an interface. |
| 1300 | * @param tree The tree to parse. normalInterfaceDeclaration should be the root value. |
| 1301 | * @param containingClass ClassInfo that contains the interface declaration. |
| 1302 | * null if the interface is a root class. |
| 1303 | * @return a ClassInfo representing the interface. |
| 1304 | */ |
| 1305 | private ClassInfo buildInterface(ParseTree tree, ClassInfo containingClass) { |
| 1306 | @SuppressWarnings("unchecked") |
| 1307 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 1308 | ParseTree child = it.next(); |
| 1309 | |
| 1310 | // parse modifiers and get comment and position |
| 1311 | Modifiers modifiers = new Modifiers(this); |
| 1312 | modifiers.parseModifiers(child); |
| 1313 | CommentAndPosition commentAndPosition = parseCommentAndPosition(tree); |
| 1314 | |
| 1315 | it.next(); |
| 1316 | child = it.next(); |
| 1317 | |
| 1318 | // get class name |
| 1319 | ClassInfo iface = buildClassName(child, containingClass, modifiers, |
| 1320 | commentAndPosition.getCommentText(), |
| 1321 | commentAndPosition.getPosition(), ClassType.INTERFACE); |
| 1322 | |
| 1323 | child = it.next(); |
| 1324 | |
| 1325 | // parse generics if they exist |
| 1326 | if ("typeParameters".equals(child.toString())) { |
| 1327 | iface.type().setTypeArguments(buildTypeVariables(child)); |
| 1328 | child = it.next(); |
| 1329 | } |
| 1330 | |
| 1331 | // parse interfaces implemented by this interface |
| 1332 | if ("extends".equals(child.toString())) { |
| 1333 | child = it.next(); |
| 1334 | |
| 1335 | parseInterfaces(child, iface); |
| 1336 | |
| 1337 | child = it.next(); |
| 1338 | } |
| 1339 | |
| 1340 | // finally, build the body of the interface |
| 1341 | buildInterfaceBody(child, iface); |
| 1342 | |
| 1343 | return iface; |
| 1344 | } |
| 1345 | |
| 1346 | /** |
| 1347 | * Parses the body of the interface, adding it to iface. |
| 1348 | * @param tree The tree to parse. interfaceBody should be the root value. |
| 1349 | * @param iface ClassInfo that will contain all of the interface body. |
| 1350 | */ |
| 1351 | private void buildInterfaceBody(ParseTree tree, ClassInfo iface) { |
| 1352 | for (Object o : tree.getChildren()) { |
| 1353 | if (!o.toString().equals("interfaceBodyDeclaration")) { |
| 1354 | continue; |
| 1355 | } |
| 1356 | |
| 1357 | ParseTree child = (ParseTree) ((ParseTree) o).getChild(0); |
| 1358 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1359 | if (";".equals(child.toString())) { |
| 1360 | continue; |
| 1361 | } |
| 1362 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1363 | // field |
| 1364 | if ("interfaceFieldDeclaration".equals(child.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1365 | for (FieldInfo f : buildFields(child, iface)) { |
| 1366 | iface.addField(f); |
| 1367 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1368 | // method |
| 1369 | } else if ("interfaceMethodDeclaration".equals(child.toString())) { |
| 1370 | iface.addMethod(buildMethod(child, iface, false)); |
| 1371 | // inner class |
| 1372 | } else if ("normalClassDeclaration".equals(child.getChild(0).toString())) { |
| 1373 | iface.addInnerClass(buildClass((ParseTree) child.getChild(0), iface)); |
| 1374 | // inner enum |
| 1375 | } else if ("enumDeclaration".equals(child.getChild(0).toString())) { |
| 1376 | iface.addInnerClass(buildEnum((ParseTree) child.getChild(0), iface)); |
| 1377 | // inner interface |
| 1378 | } else if ("normalInterfaceDeclaration".equals(child.getChild(0).toString())) { |
| 1379 | iface.addInnerClass(buildInterface((ParseTree) child.getChild(0), iface)); |
| 1380 | // inner annotation |
| 1381 | } else if ("annotationTypeDeclaration".equals(child.getChild(0).toString())) { |
| 1382 | iface.addInnerClass(buildAnnotationDeclaration( |
| 1383 | (ParseTree) child.getChild(0), iface)); |
| 1384 | } |
| 1385 | } |
| 1386 | } |
| 1387 | |
| 1388 | /** |
| 1389 | * Builds a ClassInfo of an annotation declaration. |
| 1390 | * @param tree The tree to parse. annotationTypeDeclaration should be the root value. |
| 1391 | * @param containingClass The class that contains this annotation. |
| 1392 | * null if this is a root annotation. |
| 1393 | * @return the ClassInfo of the annotation declaration. |
| 1394 | */ |
| 1395 | private ClassInfo buildAnnotationDeclaration(ParseTree tree, ClassInfo containingClass) { |
| 1396 | @SuppressWarnings("unchecked") |
| 1397 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 1398 | ParseTree child = it.next(); |
| 1399 | |
| 1400 | // get comment and position |
| 1401 | CommentAndPosition commentAndPosition = parseCommentAndPosition(tree); |
| 1402 | |
| 1403 | // modifiers |
| 1404 | Modifiers modifiers = new Modifiers(this); |
| 1405 | modifiers.parseModifiers(child); |
| 1406 | |
| 1407 | // three calls to next to skip over @, interface and then |
| 1408 | // make child = the name of this annotation |
| 1409 | it.next(); |
| 1410 | it.next(); |
| 1411 | child = it.next(); |
| 1412 | |
| 1413 | // build class name and initialize the class |
| 1414 | ClassInfo annotation = buildClassName(child, containingClass, modifiers, |
| 1415 | commentAndPosition.getCommentText(), |
| 1416 | commentAndPosition.getPosition(), ClassType.INTERFACE); |
| 1417 | |
| 1418 | child = it.next(); |
| 1419 | |
| 1420 | // build annotation body |
| 1421 | buildAnnotationBody(child, annotation); |
| 1422 | |
| 1423 | return annotation; |
| 1424 | } |
| 1425 | |
| 1426 | /** |
| 1427 | * Parses the body of the annotation declaration. |
| 1428 | * @param tree The tree to parse. annotationTypeBody should be the root value. |
| 1429 | * @param annotation the Classinfo in which the annotation elements should be added. |
| 1430 | */ |
| 1431 | private void buildAnnotationBody(ParseTree tree, ClassInfo annotation) { |
| 1432 | for (Object o : tree.getChildren()) { |
| 1433 | if (!"annotationTypeElementDeclaration".equals(o.toString())) { |
| 1434 | continue; |
| 1435 | } |
| 1436 | |
| 1437 | ParseTree child = (ParseTree) ((ParseTree) o).getChild(0); |
| 1438 | |
| 1439 | // annotation fields |
| 1440 | if ("interfaceFieldDeclaration".equals(child.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1441 | for (FieldInfo f : buildFields(child, annotation)) { |
| 1442 | annotation.addField(f); |
| 1443 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1444 | // annotation methods |
| 1445 | } else if ("annotationMethodDeclaration".equals(child.toString())) { |
| 1446 | annotation.addAnnotationElement(buildMethod(child, annotation, true)); |
| 1447 | // inner class |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1448 | } else if ("normalClassDeclaration".equals(child.toString())) { |
| 1449 | annotation.addInnerClass(buildClass((ParseTree) child, annotation)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1450 | // enum |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1451 | } else if ("enumDeclaration".equals(child.toString())) { |
| 1452 | annotation.addInnerClass(buildEnum((ParseTree) child, annotation)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1453 | // inner interface |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1454 | } else if ("normalInterfaceDeclaration".equals(child.toString())) { |
| 1455 | annotation.addInnerClass(buildInterface((ParseTree) child, annotation)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1456 | // inner annotation |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1457 | } else if ("annotationTypeDeclaration".equals(child.toString())) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1458 | annotation.addInnerClass(buildAnnotationDeclaration( |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1459 | (ParseTree) child, annotation)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1460 | } |
| 1461 | } |
| 1462 | } |
| 1463 | |
| 1464 | /** |
| 1465 | * Build an annotation instance. |
| 1466 | * @param tree The tree to parse. annotation should be the root value. |
| 1467 | * @param builder InfoBuilder of this file. |
| 1468 | * @return The AnnotationInstanceInfo being parsed. |
| 1469 | */ |
| 1470 | private static AnnotationInstanceInfo buildAnnotationInstance(ParseTree tree, |
| 1471 | InfoBuilder builder) { |
| 1472 | @SuppressWarnings("unchecked") |
| 1473 | Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator(); |
| 1474 | |
| 1475 | AnnotationInstanceInfo annotationInstance = new AnnotationInstanceInfo(); |
| 1476 | |
| 1477 | it.next(); |
| 1478 | |
| 1479 | // parse the name, get its full version, and then get the ClassInfo of it, if possible. |
| 1480 | String name = InfoBuilder.buildQualifiedName(it.next()); |
| 1481 | StringBuilder qualifiedNameBuilder = new StringBuilder(); |
| 1482 | resolveQualifiedName(name, qualifiedNameBuilder, builder); |
| 1483 | |
| 1484 | if ("".equals(qualifiedNameBuilder.toString())) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1485 | addFutureResolution(annotationInstance, "annotationTypeName", name, builder); |
| 1486 | annotationInstance.setSimpleAnnotationName(name); // TODO - remove once we've completed the parser |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1487 | } else { // can't have generics here so we won't do a test |
| 1488 | annotationInstance.setClass(Caches.obtainClass(qualifiedNameBuilder.toString())); |
| 1489 | } |
| 1490 | |
| 1491 | // at this point, the annotation is either finished or we have more work to do |
| 1492 | if (!it.hasNext()) { |
| 1493 | return annotationInstance; |
| 1494 | } |
| 1495 | |
| 1496 | it.next(); |
| 1497 | ParseTree child = it.next(); |
| 1498 | |
| 1499 | // parse elementValue pairs |
| 1500 | if ("elementValuePairs".equals(child.toString())) { |
| 1501 | for (Object o : child.getChildren()) { |
| 1502 | if (!"elementValuePair".equals(o.toString())) { |
| 1503 | continue; |
| 1504 | } |
| 1505 | |
| 1506 | ParseTree inner = (ParseTree) o; |
| 1507 | MethodInfo element = null; |
| 1508 | String methodName = inner.getChild(0).toString(); |
| 1509 | |
| 1510 | // try and look up the MethodInfo for this annotation, if possible |
| 1511 | if (annotationInstance.type() != null) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1512 | for (MethodInfo m : annotationInstance.type().annotationElements()) { |
| 1513 | if (methodName.equals(m.name()) || |
| 1514 | annotationInstance.type().annotationElements().size() == 1) { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1515 | element = m; |
| 1516 | break; |
| 1517 | } |
| 1518 | } |
| 1519 | } |
| 1520 | |
| 1521 | // go to elementValue |
| 1522 | AnnotationValueInfo info = buildElementValue( |
| 1523 | (ParseTree) inner.getChild(2), builder); |
| 1524 | |
| 1525 | if (element == null) { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1526 | addFutureResolution(info, "element", methodName, builder); |
| 1527 | info.setAnnotationInstanceName(name); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1528 | } else { |
| 1529 | info.setElement(element); |
| 1530 | } |
| 1531 | |
| 1532 | annotationInstance.addElementValue(info); |
| 1533 | } |
| 1534 | // parse element value |
| 1535 | } else if ("elementValue".equals(child.toString())) { |
| 1536 | annotationInstance.addElementValue(buildElementValue(child, builder)); |
| 1537 | } |
| 1538 | |
| 1539 | return annotationInstance; |
| 1540 | } |
| 1541 | |
| 1542 | /** |
| 1543 | * Builds the value of the annotation element. |
| 1544 | * @param tree The tree to parse. elementValue should be the root value. |
| 1545 | * @param builder InfoBuilder of this file. |
| 1546 | * @return AnnotationValueInfo representing the elementValue. |
| 1547 | */ |
| 1548 | private static AnnotationValueInfo buildElementValue(ParseTree tree, InfoBuilder builder) { |
| 1549 | AnnotationValueInfo elementValue = new AnnotationValueInfo(); |
| 1550 | Object value = null; |
| 1551 | |
| 1552 | // parse some stuff |
| 1553 | String str = tree.getChild(0).toString(); |
| 1554 | if ("conditionalExpression".equals(str)) { |
| 1555 | value = parseExpression((ParseTree) tree.getChild(0)); |
| 1556 | } else if ("annotation".equals(str)) { |
| 1557 | value = InfoBuilder.buildAnnotationInstance((ParseTree) tree.getChild(0), builder); |
| 1558 | } else if ("elementValueArrayInitializer".equals(str)) { |
| 1559 | ParseTree child = (ParseTree) tree.getChild(0); |
| 1560 | ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>(); |
| 1561 | for (Object o : child.getChildren()) { |
| 1562 | if ("elementValue".equals(o.toString())) { |
| 1563 | values.add(buildElementValue((ParseTree) o, builder)); |
| 1564 | } |
| 1565 | } |
| 1566 | |
| 1567 | value = values; |
| 1568 | } |
| 1569 | |
| 1570 | elementValue.init(value); |
| 1571 | |
| 1572 | return elementValue; |
| 1573 | } |
| 1574 | |
| 1575 | /** |
| 1576 | * Get the dimensions of the type, as a String. |
| 1577 | * @param tree The tree to parse. type should be the root value. |
| 1578 | * @return A String of the dimensions of the type. |
| 1579 | */ |
| 1580 | private String getDimensions(ParseTree tree) { |
| 1581 | // we only have dimensions if the count is not 1 |
| 1582 | if (tree.getChildCount() == 1) { |
| 1583 | return null; |
| 1584 | } |
| 1585 | |
| 1586 | StringBuilder builder = new StringBuilder(); |
| 1587 | |
| 1588 | for (int i = 1; i < tree.getChildCount(); i++) { |
| 1589 | builder.append(((ParseTree) tree.getChild(i)).toString()); |
| 1590 | } |
| 1591 | |
| 1592 | return builder.toString(); |
| 1593 | } |
| 1594 | |
| 1595 | /** |
| 1596 | * When we have data that we can't yet parse, save it for later. |
| 1597 | * @param resolvable Resolvable to which the data refers. |
| 1598 | * @param variable Variable in the document to which the data refers; |
| 1599 | * @param value Value for the variable |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1600 | * @param builder The InfoBuilder of this file |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1601 | */ |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1602 | private static void addFutureResolution(Resolvable resolvable, String variable, |
| 1603 | String value, InfoBuilder builder) { |
| 1604 | resolvable.addResolution(new Resolution(variable, value, builder)); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1605 | |
| 1606 | Caches.addResolvableToCache(resolvable); |
| 1607 | } |
| 1608 | |
| 1609 | /** |
| 1610 | * Turns a short name of a class into the qualified name of a class. |
| 1611 | * StringBuilder will contain an empty string if not found. |
| 1612 | * @param name the abbreviated name of the class |
| 1613 | * @param qualifiedClassName the qualified name that will be set if found. |
| 1614 | * Unchanged if not found. |
| 1615 | * @param builder InfoBuilder with all of the file specific information necessary |
| 1616 | * to properly resolve the name. |
| 1617 | * @return a boolean is returned that will be true if the type is a generic. false otherwise. |
| 1618 | */ |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1619 | public static boolean resolveQualifiedName(String name, |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1620 | StringBuilder qualifiedClassName, |
| 1621 | InfoBuilder builder) { |
| 1622 | // steps to figure out a class's real name |
| 1623 | // check class(es) in this file |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1624 | |
| 1625 | // trying something out. let's see how this works |
| 1626 | if (name.indexOf('.') != -1) { |
| 1627 | qualifiedClassName.append(name); |
| 1628 | return false; |
| 1629 | } |
| 1630 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1631 | // TODO - search since we're now a HashSet |
| 1632 | for (String className : builder.getClassNames()) { |
| 1633 | int beginIndex = className.lastIndexOf(".") + 1; |
| 1634 | |
| 1635 | if (className.substring(beginIndex).equals(name)) { |
| 1636 | qualifiedClassName.append(className); |
| 1637 | return qualifiedClassName.toString().equals(name); |
| 1638 | } |
| 1639 | } |
| 1640 | |
| 1641 | // check package |
| 1642 | ClassInfo potentialClass = builder.getPackage().getClass(name); |
| 1643 | |
| 1644 | if (potentialClass != null) { |
| 1645 | qualifiedClassName.append(potentialClass.qualifiedName()); |
| 1646 | return qualifiedClassName.toString().equals(name); |
| 1647 | } |
| 1648 | |
| 1649 | potentialClass = null; |
| 1650 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1651 | String potentialName = null; |
| 1652 | // check superclass and interfaces for type |
| 1653 | if (builder.getRootClass() != null) { |
| 1654 | potentialName = resolveQualifiedNameInInheritedClass(name, builder.getRootClass(), |
| 1655 | builder.getRootClass().containingPackage().name()); |
| 1656 | } |
| 1657 | |
| 1658 | if (potentialName != null) { |
| 1659 | qualifiedClassName.append(potentialName); |
| 1660 | return false; |
| 1661 | } |
| 1662 | |
| 1663 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1664 | // check class imports - ie, java.lang.String; |
| 1665 | ArrayList<String> packagesToCheck = new ArrayList<String>(); |
| 1666 | for (String imp : builder.getImports()) { |
| 1667 | // +1 to get rid of off by 1 error |
| 1668 | String endOfName = imp.substring(imp.lastIndexOf('.') + 1); |
| 1669 | if (endOfName.equals(name) || (name.indexOf('.') != -1 && |
| 1670 | endOfName.equals( |
| 1671 | name.substring(0, name.lastIndexOf('.'))))) { |
| 1672 | qualifiedClassName.append(imp); |
| 1673 | return qualifiedClassName.toString().equals(name); |
| 1674 | } else if (endOfName.equals("*")) { |
| 1675 | // add package to check |
| 1676 | packagesToCheck.add(imp.substring(0, imp.lastIndexOf('.'))); |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1677 | } else { |
| 1678 | // check inner classes |
| 1679 | ClassInfo cl = Caches.obtainClass(imp); |
| 1680 | String possibleName = resolveQualifiedInnerName(cl.qualifiedName() + "." + name, |
| 1681 | cl); |
| 1682 | if (possibleName != null) { |
| 1683 | qualifiedClassName.append(possibleName); |
| 1684 | return false; |
| 1685 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1686 | } |
| 1687 | } |
| 1688 | |
| 1689 | // check package imports - ie, java.lang.*; |
| 1690 | for (String packageName : packagesToCheck) { |
| 1691 | PackageInfo pkg = Caches.obtainPackage(packageName); |
| 1692 | |
| 1693 | ClassInfo cls = pkg.getClass(name); |
| 1694 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1695 | if (cls != null && name.equals(cls.name())) { |
| 1696 | qualifiedClassName.append(cls.qualifiedName()); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1697 | return qualifiedClassName.toString().equals(name); |
| 1698 | } |
| 1699 | } |
| 1700 | // including import's inner classes... |
| 1701 | // check package of imports... |
| 1702 | |
| 1703 | // TODO - remove |
| 1704 | // FROM THE JAVADOC VERSION OF THIS FUNCTION |
| 1705 | // Find the specified class or interface within the context of this class doc. |
| 1706 | // Search order: 1) qualified name, 2) nested in this class or interface, |
| 1707 | // 3) in this package, 4) in the class imports, 5) in the package imports. |
| 1708 | // Return the ClassDoc if found, null if not found. |
| 1709 | |
| 1710 | return false; |
| 1711 | } |
| 1712 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1713 | private static String resolveQualifiedNameInInheritedClass(String name, ClassInfo cl, |
| 1714 | String originalPackage) { |
| 1715 | ArrayList<ClassInfo> classesToCheck = new ArrayList<ClassInfo>(); |
| 1716 | if (cl != null) { |
| 1717 | // if we're in a new package only, check it |
| 1718 | if (cl.containingPackage() != null && |
| 1719 | !originalPackage.equals(cl.containingPackage().name())) { |
| 1720 | // check for new class |
| 1721 | ClassInfo cls = cl.containingPackage().getClass(name); |
| 1722 | |
| 1723 | if (cls != null && name.equals(cls.name())) { |
| 1724 | return cls.name(); |
| 1725 | } |
| 1726 | } |
| 1727 | |
| 1728 | if (cl.realSuperclass() != null) { |
| 1729 | classesToCheck.add(cl.realSuperclass()); |
| 1730 | } |
| 1731 | |
| 1732 | if (cl.realInterfaces() != null) { |
| 1733 | for (ClassInfo iface : cl.realInterfaces()) { |
| 1734 | classesToCheck.add(iface); |
| 1735 | } |
| 1736 | } |
| 1737 | |
| 1738 | for (ClassInfo cls : classesToCheck) { |
| 1739 | String potential = resolveQualifiedNameInInheritedClass(name, cls, originalPackage); |
| 1740 | |
| 1741 | if (potential != null) { |
| 1742 | return potential; |
| 1743 | } |
| 1744 | } |
| 1745 | } |
| 1746 | return null; |
| 1747 | } |
| 1748 | |
| 1749 | private static String resolveQualifiedInnerName(String possibleQualifiedName, ClassInfo cl) { |
| 1750 | if (cl.innerClasses() == null) { |
| 1751 | return null; |
| 1752 | } |
| 1753 | |
| 1754 | for (ClassInfo inner : cl.innerClasses()) { |
| 1755 | if (possibleQualifiedName.equals(inner.qualifiedName())) { |
| 1756 | return possibleQualifiedName; |
| 1757 | } |
| 1758 | |
| 1759 | String name = resolveQualifiedInnerName(possibleQualifiedName + "." + inner.name(), |
| 1760 | inner); |
| 1761 | |
| 1762 | if (name != null) { |
| 1763 | return name; |
| 1764 | } |
| 1765 | } |
| 1766 | |
| 1767 | return null; |
| 1768 | } |
| 1769 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1770 | /** |
| 1771 | * Parses the tree, looking for the comment and position. |
| 1772 | * @param tree The tree to parse. |
| 1773 | * @return a CommentAndPosition object containing the comment and position of the element. |
| 1774 | */ |
| 1775 | private CommentAndPosition parseCommentAndPosition(ParseTree tree) { |
| 1776 | Tree child = tree.getChild(0).getChild(0); |
| 1777 | |
| 1778 | // three options (modifiers with annotations, modifiers w/o annotations, no modifiers) |
| 1779 | // if there are no modifiers, use tree.getChild(1) |
| 1780 | // otherwise, dive as deep as possible into modifiers to get to the comment and position. |
| 1781 | child = ("<epsilon>".equals(child.toString())) ? tree.getChild(1) : child; |
| 1782 | |
| 1783 | while (child.getChildCount() > 0) { |
| 1784 | child = child.getChild(0); |
| 1785 | } |
| 1786 | |
| 1787 | CommentAndPosition cAndP = new CommentAndPosition(); |
| 1788 | cAndP.setCommentText((ParseTree) child); |
| 1789 | cAndP.setPosition((ParseTree) child); |
| 1790 | return cAndP; |
| 1791 | } |
| 1792 | |
| 1793 | /** |
| 1794 | * Private class to facilitate passing the comment and position out of a function. |
| 1795 | */ |
| 1796 | private class CommentAndPosition { |
| 1797 | public String getCommentText() { |
| 1798 | return mCommentText; |
| 1799 | } |
| 1800 | |
| 1801 | /** |
| 1802 | * Parses the tree to get the commentText and set that value. |
| 1803 | * @param tree The tree to parse. Should be pointing to the node containing the comment. |
| 1804 | */ |
| 1805 | public void setCommentText(ParseTree tree) { |
| 1806 | if (tree.hiddenTokens != null && !tree.hiddenTokens.isEmpty()) { |
| 1807 | mCommentText = ((CommonToken) tree.hiddenTokens.get(0)).getText(); |
| 1808 | |
| 1809 | if (mCommentText != null) { |
| 1810 | return; |
| 1811 | } |
| 1812 | } |
| 1813 | |
| 1814 | mCommentText = ""; |
| 1815 | } |
| 1816 | |
| 1817 | public SourcePositionInfo getPosition() { |
| 1818 | return mPosition; |
| 1819 | } |
| 1820 | |
| 1821 | /** |
| 1822 | * Parses the tree to get the SourcePositionInfo of the node. |
| 1823 | * @param tree The tree to parse. Should be pointing to the node containing the position. |
| 1824 | */ |
| 1825 | public void setPosition(ParseTree tree) { |
| 1826 | CommonToken token = (CommonToken) tree.payload; |
| 1827 | |
| 1828 | int line = token.getLine(); |
| 1829 | int column = token.getCharPositionInLine(); |
| 1830 | String fileName = ((ANTLRFileStream) token.getInputStream()).getSourceName(); |
| 1831 | |
| 1832 | mPosition = new SourcePositionInfo(fileName, line, column); |
| 1833 | } |
| 1834 | |
| 1835 | private String mCommentText; |
| 1836 | private SourcePositionInfo mPosition; |
| 1837 | } |
| 1838 | |
| 1839 | /** |
| 1840 | * Private class to handle all the possible modifiers to a class/interface/field/anything else. |
| 1841 | */ |
| 1842 | private class Modifiers { |
| 1843 | private boolean mIsPublic = false; |
| 1844 | private boolean mIsProtected = false; |
| 1845 | private boolean mIsPackagePrivate = true; |
| 1846 | private boolean mIsPrivate = false; |
| 1847 | private boolean mIsStatic = false; |
| 1848 | private boolean mIsAbstract = false; |
| 1849 | private boolean mIsFinal = false; |
| 1850 | private boolean mIsTransient = false; |
| 1851 | private boolean mIsVolatile = false; |
| 1852 | private boolean mIsSynthetic = false; |
| 1853 | private boolean mIsSynchronized = false; |
| 1854 | private boolean mIsStrictfp = false; |
| 1855 | private InfoBuilder mBuilder; |
| 1856 | private ArrayList<AnnotationInstanceInfo> mAnnotations; |
| 1857 | |
| 1858 | public Modifiers(InfoBuilder builder) { |
| 1859 | mAnnotations = new ArrayList<AnnotationInstanceInfo>(); |
| 1860 | mBuilder = builder; |
| 1861 | } |
| 1862 | |
| 1863 | /** |
| 1864 | * Parses all of the modifiers of any declaration, including annotations. |
| 1865 | * @param tree |
| 1866 | */ |
| 1867 | public void parseModifiers(ParseTree tree) { |
| 1868 | for (Object child : tree.getChildren()) { |
| 1869 | String modifier = child.toString(); |
| 1870 | |
| 1871 | if ("public".equals(modifier)) { |
| 1872 | mIsPublic = true; |
| 1873 | mIsPackagePrivate = false; |
| 1874 | } else if ("protected".equals(modifier)) { |
| 1875 | mIsProtected = true; |
| 1876 | mIsPackagePrivate = false; |
| 1877 | } else if ("private".equals(modifier)) { |
| 1878 | mIsPrivate = true; |
| 1879 | mIsPackagePrivate = false; |
| 1880 | } else if ("static".equals(modifier)) { |
| 1881 | mIsStatic = true; |
| 1882 | } else if ("abstract".equals(modifier)) { |
| 1883 | mIsAbstract = true; |
| 1884 | } else if ("final".equals(modifier)) { |
| 1885 | mIsFinal = true; |
| 1886 | } else if ("transient".equals(modifier)) { |
| 1887 | mIsTransient = true; |
| 1888 | } else if ("volatile".equals(modifier)) { |
| 1889 | mIsVolatile = true; |
| 1890 | } else if ("synthetic".equals(modifier)) { |
| 1891 | mIsSynthetic = true; |
| 1892 | } else if ("synchronized".equals(modifier)) { |
| 1893 | mIsSynchronized = true; |
| 1894 | } else if ("strictfp".equals(modifier)) { |
| 1895 | mIsStrictfp = true; |
| 1896 | } else if ("annotation".equals(modifier)) { |
| 1897 | mAnnotations.add(buildAnnotationInstance((ParseTree) child, mBuilder)); |
| 1898 | } |
| 1899 | } |
| 1900 | } |
| 1901 | |
| 1902 | public boolean isPublic() { |
| 1903 | return mIsPublic; |
| 1904 | } |
| 1905 | |
| 1906 | public boolean isProtected() { |
| 1907 | return mIsProtected; |
| 1908 | } |
| 1909 | |
| 1910 | public boolean isPackagePrivate() { |
| 1911 | return mIsPackagePrivate; |
| 1912 | } |
| 1913 | |
| 1914 | public boolean isPrivate() { |
| 1915 | return mIsPrivate; |
| 1916 | } |
| 1917 | |
| 1918 | public boolean isStatic() { |
| 1919 | return mIsStatic; |
| 1920 | } |
| 1921 | |
| 1922 | public boolean isAbstract() { |
| 1923 | return mIsAbstract; |
| 1924 | } |
| 1925 | |
| 1926 | public boolean isFinal() { |
| 1927 | return mIsFinal; |
| 1928 | } |
| 1929 | |
| 1930 | public boolean isTransient() { |
| 1931 | return mIsTransient; |
| 1932 | } |
| 1933 | |
| 1934 | public boolean isVolatile() { |
| 1935 | return mIsVolatile; |
| 1936 | } |
| 1937 | |
| 1938 | public boolean isSynthetic() { |
| 1939 | return mIsSynthetic; |
| 1940 | } |
| 1941 | |
| 1942 | public boolean isSynchronized() { |
| 1943 | return mIsSynchronized; |
| 1944 | } |
| 1945 | |
| 1946 | @SuppressWarnings("unused") |
| 1947 | public boolean isStrictfp() { |
| 1948 | return mIsStrictfp; |
| 1949 | } |
| 1950 | |
| 1951 | public ArrayList<AnnotationInstanceInfo> getAnnotations() { |
| 1952 | return mAnnotations; |
| 1953 | } |
| 1954 | }; |
| 1955 | |
| 1956 | |
| 1957 | /** |
| 1958 | * Singleton class to store all of the global data amongst every InfoBuilder. |
| 1959 | */ |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1960 | public static class Caches { |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1961 | private static HashMap<String, PackageInfo> mPackages |
| 1962 | = new HashMap<String, PackageInfo>(); |
| 1963 | private static HashMap<String, ClassInfo> mClasses |
| 1964 | = new HashMap<String, ClassInfo>(); |
| 1965 | private static HashSet<Resolvable> mInfosToResolve |
| 1966 | = new HashSet<Resolvable>(); |
| 1967 | |
| 1968 | public static PackageInfo obtainPackage(String packageName) { |
| 1969 | PackageInfo pkg = mPackages.get(packageName); |
| 1970 | |
| 1971 | if (pkg == null) { |
| 1972 | pkg = new PackageInfo(packageName); |
| 1973 | mPackages.put(packageName, pkg); |
| 1974 | } |
| 1975 | |
| 1976 | return pkg; |
| 1977 | } |
| 1978 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1979 | /** |
| 1980 | * Gets the ClassInfo from the master list or creates a new one if it does not exist. |
| 1981 | * @param qualifiedClassName Qualified name of the ClassInfo to obtain. |
| 1982 | * @return the ClassInfo |
| 1983 | */ |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1984 | public static ClassInfo obtainClass(String qualifiedClassName) { |
| 1985 | ClassInfo cls = mClasses.get(qualifiedClassName); |
| 1986 | |
| 1987 | if (cls == null) { |
| 1988 | cls = new ClassInfo(qualifiedClassName); |
| 1989 | mClasses.put(cls.qualifiedName(), cls); |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 1990 | } |
| 1991 | |
| 1992 | return cls; |
| 1993 | } |
| 1994 | |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 1995 | /** |
| 1996 | * Gets the ClassInfo from the master list or returns null if it does not exist. |
| 1997 | * @param qualifiedClassName Qualified name of the ClassInfo to obtain. |
| 1998 | * @return the ClassInfo or null, if the ClassInfo does not exist. |
| 1999 | */ |
| 2000 | public static ClassInfo getClass(String qualifiedClassName) { |
| 2001 | return mClasses.get(qualifiedClassName); |
| 2002 | } |
| 2003 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 2004 | public static void addResolvableToCache(Resolvable resolvable) { |
| 2005 | mInfosToResolve.add(resolvable); |
| 2006 | } |
| 2007 | |
| 2008 | public static void printResolutions() { |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 2009 | if (mInfosToResolve.isEmpty()) { |
| 2010 | System.out.println("We've resolved everything."); |
| 2011 | return; |
| 2012 | } |
| 2013 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 2014 | for (Resolvable r : mInfosToResolve) { |
| 2015 | r.printResolutions(); |
| 2016 | System.out.println(); |
| 2017 | } |
| 2018 | } |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 2019 | |
| 2020 | public static void resolve() { |
| 2021 | HashSet<Resolvable> resolveList = mInfosToResolve; |
| 2022 | mInfosToResolve = new HashSet<Resolvable>(); |
| 2023 | |
| 2024 | for (Resolvable r : resolveList) { |
| 2025 | // if we could not resolve everything in this class |
| 2026 | if (!r.resolveResolutions()) { |
| 2027 | mInfosToResolve.add(r); |
| 2028 | } |
| 2029 | |
| 2030 | System.out.println(); |
| 2031 | } |
| 2032 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 2033 | } |
| 2034 | |
| 2035 | public PackageInfo getPackage() { |
| 2036 | return mPackage; |
| 2037 | } |
| 2038 | |
| 2039 | public ArrayList<String> getImports() { |
| 2040 | return mImports; |
| 2041 | } |
| 2042 | |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 2043 | public HashSet<String> getClassNames() { |
| 2044 | return mClassNames; |
| 2045 | } |
Andrew Sapperstein | 6ba612e | 2011-06-20 18:41:24 -0700 | [diff] [blame] | 2046 | |
| 2047 | public ClassInfo getRootClass() { |
| 2048 | return mRootClass; |
| 2049 | } |
Andrew Sapperstein | d6eaacb | 2011-05-20 13:14:56 -0700 | [diff] [blame] | 2050 | } |