1 | /******************************************************************************* |
2 | * Copyright (c) 2007, 2009 IBM Corporation and others. |
3 | * All rights reserved. This program and the accompanying materials |
4 | * are made available under the terms of the Eclipse Public License v1.0 |
5 | * which accompanies this distribution, and is available at |
6 | * http://www.eclipse.org/legal/epl-v10.html |
7 | * |
8 | * Contributors: |
9 | * IBM Corporation - initial API and implementation |
10 | *******************************************************************************/ |
11 | package org.eclipse.pde.api.tools.internal.provisional.scanner; |
12 | |
13 | import java.io.FileNotFoundException; |
14 | import java.io.IOException; |
15 | import java.io.InputStream; |
16 | import java.text.MessageFormat; |
17 | import java.util.ArrayList; |
18 | import java.util.Iterator; |
19 | import java.util.List; |
20 | import java.util.Map; |
21 | |
22 | import org.eclipse.core.runtime.CoreException; |
23 | import org.eclipse.core.runtime.IProgressMonitor; |
24 | import org.eclipse.core.runtime.IStatus; |
25 | import org.eclipse.core.runtime.Status; |
26 | import org.eclipse.core.runtime.SubMonitor; |
27 | import org.eclipse.jdt.core.Flags; |
28 | import org.eclipse.jdt.core.ICompilationUnit; |
29 | import org.eclipse.jdt.core.JavaCore; |
30 | import org.eclipse.jdt.core.dom.AST; |
31 | import org.eclipse.jdt.core.dom.ASTNode; |
32 | import org.eclipse.jdt.core.dom.ASTParser; |
33 | import org.eclipse.jdt.core.dom.ASTVisitor; |
34 | import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; |
35 | import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; |
36 | import org.eclipse.jdt.core.dom.BodyDeclaration; |
37 | import org.eclipse.jdt.core.dom.EnumDeclaration; |
38 | import org.eclipse.jdt.core.dom.FieldDeclaration; |
39 | import org.eclipse.jdt.core.dom.Javadoc; |
40 | import org.eclipse.jdt.core.dom.MethodDeclaration; |
41 | import org.eclipse.jdt.core.dom.Name; |
42 | import org.eclipse.jdt.core.dom.PackageDeclaration; |
43 | import org.eclipse.jdt.core.dom.SimpleName; |
44 | import org.eclipse.jdt.core.dom.TagElement; |
45 | import org.eclipse.jdt.core.dom.TypeDeclaration; |
46 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; |
47 | import org.eclipse.pde.api.tools.internal.CompilationUnit; |
48 | import org.eclipse.pde.api.tools.internal.JavadocTagManager; |
49 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
50 | import org.eclipse.pde.api.tools.internal.provisional.Factory; |
51 | import org.eclipse.pde.api.tools.internal.provisional.IApiDescription; |
52 | import org.eclipse.pde.api.tools.internal.provisional.IApiJavadocTag; |
53 | import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; |
54 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; |
55 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor; |
56 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor; |
57 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor; |
58 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod; |
59 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; |
60 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer; |
61 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot; |
62 | import org.eclipse.pde.api.tools.internal.util.Signatures; |
63 | import org.eclipse.pde.api.tools.internal.util.Util; |
64 | |
65 | /** |
66 | * Scans the source of a *.java file for any API javadoc tags |
67 | * @since 1.0.0 |
68 | */ |
69 | public class TagScanner { |
70 | |
71 | /** |
72 | * Constant used for controlling tracing in the scanner |
73 | */ |
74 | private static boolean DEBUG = Util.DEBUG; |
75 | |
76 | /** |
77 | * Method used for initializing tracing in the scanner |
78 | */ |
79 | public static void setDebug(boolean debugValue) { |
80 | DEBUG = debugValue || Util.DEBUG; |
81 | } |
82 | |
83 | /** |
84 | * Visitor to scan a compilation unit. We only care about javadoc nodes that have either |
85 | * type or enum declarations as parents, so we have to override the ones we don't care |
86 | * about. |
87 | */ |
88 | static class Visitor extends ASTVisitor { |
89 | |
90 | private IApiDescription fDescription = null; |
91 | |
92 | /** |
93 | * Package descriptor. Initialized to default package, and overridden if a package |
94 | * declaration is visited. |
95 | */ |
96 | private IPackageDescriptor fPackage = Factory.packageDescriptor(""); //$NON-NLS-1$ |
97 | |
98 | /** |
99 | * Type descriptor for type currently being visited. |
100 | */ |
101 | private IReferenceTypeDescriptor fType = null; |
102 | |
103 | /** |
104 | * Used to look up binaries when resolving method signatures, or |
105 | * <code>null</code> if not provided. |
106 | */ |
107 | private IApiTypeContainer fContainer = null; |
108 | |
109 | /** |
110 | * List of exceptions encountered, or <code>null</code> |
111 | */ |
112 | private CoreException fException; |
113 | |
114 | /** |
115 | * Constructor |
116 | * @param description API description to annotate |
117 | * @param container class file container or <code>null</code>, used |
118 | * to resolve method signatures |
119 | */ |
120 | public Visitor(IApiDescription description, IApiTypeContainer container) { |
121 | fDescription = description; |
122 | fContainer = container; |
123 | } |
124 | |
125 | /* (non-Javadoc) |
126 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Javadoc) |
127 | */ |
128 | public boolean visit(Javadoc node) { |
129 | List tags = node.tags(); |
130 | ASTNode parent = node.getParent(); |
131 | if(parent != null) { |
132 | switch(parent.getNodeType()) { |
133 | case ASTNode.TYPE_DECLARATION: { |
134 | TypeDeclaration type = (TypeDeclaration) parent; |
135 | if(type.isInterface()) { |
136 | processTags(fType, pruneTags(tags, type), IApiJavadocTag.TYPE_INTERFACE, IApiJavadocTag.MEMBER_NONE); |
137 | } |
138 | else { |
139 | processTags(fType, pruneTags(tags, type), IApiJavadocTag.TYPE_CLASS, IApiJavadocTag.MEMBER_NONE); |
140 | } |
141 | break; |
142 | } |
143 | case ASTNode.METHOD_DECLARATION: { |
144 | MethodDeclaration method = (MethodDeclaration) parent; |
145 | String signature = Signatures.getMethodSignatureFromNode(method); |
146 | if(signature != null) { |
147 | String methodname = method.getName().getFullyQualifiedName(); |
148 | int member = IApiJavadocTag.MEMBER_METHOD; |
149 | if(method.isConstructor()) { |
150 | member = IApiJavadocTag.MEMBER_CONSTRUCTOR; |
151 | methodname = "<init>"; //$NON-NLS-1$ |
152 | } |
153 | IMethodDescriptor descriptor = fType.getMethod(methodname, signature); |
154 | processTags(descriptor, pruneTags(tags, method), getEnclosingType(method), member); |
155 | } |
156 | break; |
157 | } |
158 | case ASTNode.FIELD_DECLARATION: { |
159 | FieldDeclaration field = (FieldDeclaration) parent; |
160 | List fields = field.fragments(); |
161 | VariableDeclarationFragment fragment = null; |
162 | for(Iterator iter = fields.iterator(); iter.hasNext();) { |
163 | fragment = (VariableDeclarationFragment) iter.next(); |
164 | processTags(fType.getField(fragment.getName().getFullyQualifiedName()), pruneTags(tags, field), getEnclosingType(field), IApiJavadocTag.MEMBER_FIELD); |
165 | } |
166 | break; |
167 | } |
168 | } |
169 | } |
170 | return false; |
171 | } |
172 | |
173 | private int getEnclosingType(ASTNode node) { |
174 | ASTNode lnode = node; |
175 | while (!(lnode instanceof AbstractTypeDeclaration)) { |
176 | lnode = lnode.getParent(); |
177 | } |
178 | if (lnode instanceof TypeDeclaration) { |
179 | if (((TypeDeclaration)lnode).isInterface()) { |
180 | return IApiJavadocTag.TYPE_INTERFACE; |
181 | } |
182 | } |
183 | return IApiJavadocTag.TYPE_CLASS; |
184 | } |
185 | |
186 | /** |
187 | * A type has been entered - update the type being visited. |
188 | * |
189 | * @param name name from type node |
190 | */ |
191 | private void enterType(SimpleName name) { |
192 | if (fType == null) { |
193 | fType = fPackage.getType(name.getFullyQualifiedName()); |
194 | } else { |
195 | fType = fType.getType(name.getFullyQualifiedName()); |
196 | } |
197 | } |
198 | |
199 | /** |
200 | * A type has been exited - update the type being visited. |
201 | */ |
202 | private void exitType() { |
203 | fType = fType.getEnclosingType(); |
204 | } |
205 | |
206 | /** |
207 | * Processes the tags for the given {@link IElementDescriptor} |
208 | * @param descriptor the descriptor |
209 | * @param tags the listing of tags from the AST |
210 | * @param type one of <code>CLASS</code> or <code>INTERFACE</code> |
211 | * @param member one of <code>METHOD</code> or <code>FIELD</code> or <code>NONE</code> |
212 | */ |
213 | protected void processTags(IElementDescriptor descriptor, List tags, int type, int member) { |
214 | JavadocTagManager jtm = ApiPlugin.getJavadocTagManager(); |
215 | TagElement tag = null; |
216 | String tagname = null; |
217 | int restrictions = RestrictionModifiers.NO_RESTRICTIONS; |
218 | for(Iterator iter = tags.iterator(); iter.hasNext();) { |
219 | tag = (TagElement) iter.next(); |
220 | tagname = tag.getTagName(); |
221 | restrictions |= jtm.getRestrictionsForTag(tagname, type, member); |
222 | } |
223 | if (restrictions != RestrictionModifiers.NO_RESTRICTIONS) { |
224 | IElementDescriptor ldesc = descriptor; |
225 | if (ldesc.getElementType() == IElementDescriptor.METHOD) { |
226 | try { |
227 | ldesc = resolveMethod((IMethodDescriptor)ldesc); |
228 | } catch (CoreException e) { |
229 | fException = e; |
230 | } |
231 | } |
232 | fDescription.setRestrictions(ldesc, restrictions); |
233 | } |
234 | } |
235 | |
236 | /** |
237 | * Method to post process returned flags from the {@link Javadoc} node of the element |
238 | * @param tags the tags to process |
239 | * @param element the {@link ASTNode} the tag appears on |
240 | * @return the list of valid tags to process restrictions for |
241 | */ |
242 | private List pruneTags(final List tags, ASTNode node) { |
243 | ArrayList pruned = new ArrayList(tags.size()); |
244 | TagElement tag = null; |
245 | switch(node.getNodeType()) { |
246 | case ASTNode.TYPE_DECLARATION: { |
247 | TypeDeclaration type = (TypeDeclaration) node; |
248 | int flags = type.getModifiers(); |
249 | for (Iterator iterator = tags.iterator(); iterator.hasNext();) { |
250 | tag = (TagElement) iterator.next(); |
251 | String tagname = tag.getTagName(); |
252 | if(type.isInterface() && |
253 | ("@noextend".equals(tagname) || //$NON-NLS-1$ |
254 | "@noimplement".equals(tagname))) { //$NON-NLS-1$ |
255 | pruned.add(tag); |
256 | } |
257 | else { |
258 | if("@noextend".equals(tagname)) { //$NON-NLS-1$ |
259 | if(!Flags.isFinal(flags)) { |
260 | pruned.add(tag); |
261 | continue; |
262 | } |
263 | } |
264 | if("@noinstantiate".equals(tagname)) { //$NON-NLS-1$ |
265 | if(!Flags.isAbstract(flags)) { |
266 | pruned.add(tag); |
267 | continue; |
268 | } |
269 | } |
270 | } |
271 | } |
272 | break; |
273 | } |
274 | case ASTNode.METHOD_DECLARATION: { |
275 | MethodDeclaration method = (MethodDeclaration) node; |
276 | int flags = method.getModifiers(); |
277 | for (Iterator iterator = tags.iterator(); iterator.hasNext();) { |
278 | tag = (TagElement) iterator.next(); |
279 | if("@noreference".equals(tag.getTagName())) { //$NON-NLS-1$ |
280 | pruned.add(tag); |
281 | continue; |
282 | } |
283 | if("@nooverride".equals(tag.getTagName())) { //$NON-NLS-1$ |
284 | ASTNode parent = method.getParent(); |
285 | int pflags = 0; |
286 | if(parent instanceof BodyDeclaration) { |
287 | pflags = ((BodyDeclaration)parent).getModifiers(); |
288 | } |
289 | if(!Flags.isFinal(flags) && |
290 | !Flags.isStatic(flags) && |
291 | !Flags.isFinal(pflags)) { |
292 | pruned.add(tag); |
293 | continue; |
294 | } |
295 | } |
296 | } |
297 | break; |
298 | } |
299 | case ASTNode.FIELD_DECLARATION: { |
300 | FieldDeclaration field = (FieldDeclaration) node; |
301 | for (Iterator iterator = tags.iterator(); iterator.hasNext();) { |
302 | tag = (TagElement) iterator.next(); |
303 | boolean isfinal = Flags.isFinal(field.getModifiers()); |
304 | if(isfinal || (isfinal && Flags.isStatic(field.getModifiers()))) { |
305 | break; |
306 | } |
307 | if("@noreference".equals(tag.getTagName())) { //$NON-NLS-1$ |
308 | pruned.add(tag); |
309 | break; |
310 | } |
311 | } |
312 | break; |
313 | } |
314 | } |
315 | return pruned; |
316 | } |
317 | |
318 | /** |
319 | * Returns whether to continue processing children. |
320 | * |
321 | * @return whether to continue processing children. |
322 | */ |
323 | private boolean isContinue() { |
324 | return fException == null; |
325 | } |
326 | |
327 | /** |
328 | * Returns an exception that aborted processing, or <code>null</code> if none. |
329 | * |
330 | * @return an exception that aborted processing, or <code>null</code> if none |
331 | */ |
332 | CoreException getException() { |
333 | return fException; |
334 | } |
335 | |
336 | /* (non-Javadoc) |
337 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TypeDeclaration) |
338 | */ |
339 | public boolean visit(TypeDeclaration node) { |
340 | if(isPrivate(node.getModifiers())) { |
341 | return false; |
342 | } |
343 | enterType(node.getName()); |
344 | return isContinue(); |
345 | } |
346 | /* (non-Javadoc) |
347 | * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.TypeDeclaration) |
348 | */ |
349 | public void endVisit(TypeDeclaration node) { |
350 | if(!isPrivate(node.getModifiers())) { |
351 | exitType(); |
352 | } |
353 | } |
354 | /* (non-Javadoc) |
355 | * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.AnnotationTypeDeclaration) |
356 | */ |
357 | public void endVisit(AnnotationTypeDeclaration node) { |
358 | if(!isPrivate(node.getModifiers())) { |
359 | exitType(); |
360 | } |
361 | } |
362 | /* (non-Javadoc) |
363 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.AnnotationTypeDeclaration) |
364 | */ |
365 | public boolean visit(AnnotationTypeDeclaration node) { |
366 | if(isPrivate(node.getModifiers())) { |
367 | return false; |
368 | } |
369 | enterType(node.getName()); |
370 | return isContinue(); |
371 | } |
372 | /* (non-Javadoc) |
373 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.EnumDeclaration) |
374 | */ |
375 | public boolean visit(EnumDeclaration node) { |
376 | if(isPrivate(node.getModifiers())) { |
377 | return false; |
378 | } |
379 | enterType(node.getName()); |
380 | return isContinue(); |
381 | } |
382 | /* (non-Javadoc) |
383 | * @see org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom.EnumDeclaration) |
384 | */ |
385 | public void endVisit(EnumDeclaration node) { |
386 | if(!isPrivate(node.getModifiers())) { |
387 | exitType(); |
388 | } |
389 | } |
390 | /* (non-Javadoc) |
391 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.PackageDeclaration) |
392 | */ |
393 | public boolean visit(PackageDeclaration node) { |
394 | Name name = node.getName(); |
395 | fPackage = Factory.packageDescriptor(name.getFullyQualifiedName()); |
396 | return false; |
397 | } |
398 | /* (non-Javadoc) |
399 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration) |
400 | */ |
401 | public boolean visit(MethodDeclaration node) { |
402 | if(isPrivate(node.getModifiers())) { |
403 | return false; |
404 | } |
405 | return isContinue(); |
406 | } |
407 | /* (non-Javadoc) |
408 | * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.FieldDeclaration) |
409 | */ |
410 | public boolean visit(FieldDeclaration node) { |
411 | if(isPrivate(node.getModifiers())) { |
412 | return false; |
413 | } |
414 | return isContinue(); |
415 | } |
416 | private boolean isPrivate(int flags) { |
417 | return Flags.isPrivate(flags); |
418 | } |
419 | /** |
420 | * Returns a method descriptor with a resolved signature for the given method |
421 | * descriptor with an unresolved signature. |
422 | * |
423 | * @param descriptor method to resolve |
424 | * @return resolved method descriptor or the same method descriptor if unable to |
425 | * resolve |
426 | * @exception CoreException if unable to resolve the method and a class file |
427 | * container was provided for this purpose |
428 | */ |
429 | private IMethodDescriptor resolveMethod(IMethodDescriptor descriptor) throws CoreException { |
430 | if (fContainer != null) { |
431 | IReferenceTypeDescriptor type = descriptor.getEnclosingType(); |
432 | IApiTypeRoot classFile = fContainer.findTypeRoot(type.getQualifiedName()); |
433 | if(classFile != null) { |
434 | IApiType structure = classFile.getStructure(); |
435 | if(structure != null) { |
436 | IApiMethod[] methods = structure.getMethods(); |
437 | for (int i = 0; i < methods.length; i++) { |
438 | IApiMethod method = methods[i]; |
439 | if (descriptor.getName().equals(method.getName())) { |
440 | String signature = method.getSignature(); |
441 | String descriptorSignature = descriptor.getSignature().replace('/', '.'); |
442 | if (Signatures.matchesSignatures(descriptorSignature, signature.replace('/', '.'))) { |
443 | return descriptor.getEnclosingType().getMethod(method.getName(), signature); |
444 | } |
445 | String genericSignature = method.getGenericSignature(); |
446 | if (genericSignature != null) { |
447 | if (Signatures.matchesSignatures(descriptorSignature, genericSignature.replace('/', '.'))) { |
448 | return descriptor.getEnclosingType().getMethod(method.getName(), signature); |
449 | } |
450 | } |
451 | } |
452 | } |
453 | } |
454 | } |
455 | throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, |
456 | MessageFormat.format("Unable to resolve method signature: {0}", new String[]{descriptor.toString()}), null)); //$NON-NLS-1$ |
457 | } |
458 | return descriptor; |
459 | } |
460 | |
461 | } |
462 | |
463 | /** |
464 | * The singleton instance of the scanner |
465 | */ |
466 | private static TagScanner fSingleton = null; |
467 | |
468 | /** |
469 | * Delegate for getting the singleton instance of the scanner |
470 | * @return |
471 | */ |
472 | public static final TagScanner newScanner() { |
473 | if(fSingleton == null) { |
474 | fSingleton = new TagScanner(); |
475 | } |
476 | return fSingleton; |
477 | } |
478 | |
479 | /** |
480 | * Constructor |
481 | * Cannot be instantiated |
482 | */ |
483 | private TagScanner() {} |
484 | |
485 | /** |
486 | * Scans the specified {@link ICompilationUnit} for contributed API Javadoc tags. |
487 | * Tags on methods will have unresolved signatures. |
488 | * @param unit the compilation unit source |
489 | * @param description the API description to annotate with any new tag rules found |
490 | * @param container optional class file container containing the class file for the given source |
491 | * that can be used to resolve method signatures if required (for tags on methods). If |
492 | * not provided (<code>null</code>), method signatures will be unresolved. |
493 | * @param monitor |
494 | * |
495 | * @throws CoreException |
496 | */ |
497 | public void scan(ICompilationUnit unit, IApiDescription description, IApiTypeContainer container, IProgressMonitor monitor) throws CoreException { |
498 | scan(new CompilationUnit(unit), description, container, unit.getJavaProject().getOptions(true), monitor); |
499 | } |
500 | |
501 | /** |
502 | * Scans the specified source {@linkplain CompilationUnit} for contributed API javadoc tags. |
503 | * Tags on methods will have unresolved signatures. |
504 | * |
505 | * @param source the source file to scan for tags |
506 | * @param description the API description to annotate with any new tag rules found |
507 | * @param container optional class file container containing the class file for the given source |
508 | * that can be used to resolve method signatures if required (for tags on methods). If |
509 | * not provided (<code>null</code>), method signatures will be unresolved. |
510 | * @param options a map of Java compiler options to use when creating the AST to scan |
511 | * or <code>null</code> if default options should be used |
512 | * @param monitor |
513 | * |
514 | * @throws CoreException |
515 | */ |
516 | public void scan(CompilationUnit source, IApiDescription description, IApiTypeContainer container, Map options, IProgressMonitor monitor) throws CoreException { |
517 | SubMonitor localmonitor = SubMonitor.convert(monitor, 2); |
518 | ASTParser parser = ASTParser.newParser(AST.JLS3); |
519 | InputStream inputStream = null; |
520 | try { |
521 | inputStream = source.getInputStream(); |
522 | parser.setSource(Util.getInputStreamAsCharArray(inputStream, -1, System.getProperty("file.encoding"))); //$NON-NLS-1$ |
523 | } catch (FileNotFoundException e) { |
524 | throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, |
525 | MessageFormat.format("Compilation unit source not found: {0}", new String[]{source.getName()}), e)); //$NON-NLS-1$ |
526 | } catch (IOException e) { |
527 | if (DEBUG) { |
528 | System.err.println(source.getName()); |
529 | } |
530 | throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, |
531 | MessageFormat.format("Error reading compilation unit: {0}", new String[]{source.getName()}), e)); //$NON-NLS-1$ |
532 | } finally { |
533 | if (inputStream != null) { |
534 | try { |
535 | inputStream.close(); |
536 | } catch(IOException e) { |
537 | ApiPlugin.log(e); |
538 | } |
539 | } |
540 | } |
541 | Util.updateMonitor(localmonitor); |
542 | Map loptions = options; |
543 | if(loptions == null) { |
544 | loptions = JavaCore.getOptions(); |
545 | } |
546 | loptions.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); |
547 | parser.setCompilerOptions(loptions); |
548 | org.eclipse.jdt.core.dom.CompilationUnit cunit = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(localmonitor.newChild(1)); |
549 | Visitor visitor = new Visitor(description, container); |
550 | cunit.accept(visitor); |
551 | if (visitor.getException() != null) { |
552 | throw visitor.getException(); |
553 | } |
554 | } |
555 | } |