]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / internal / ui / text / javadoc / JavadocContentAccess2.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2008, 2012 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 * Tom Hofmann, Google <eclipse@tom.eicher.name> - [hovering] NPE when hovering over @value reference within a type's javadoc - https://bugs.eclipse.org/bugs/show_bug.cgi?id=320084
11 *******************************************************************************/
12package org.eclipse.jdt.internal.ui.text.javadoc;
13
14import java.io.IOException;
15import java.io.Reader;
16import java.net.URISyntaxException;
17import java.net.URL;
18import java.text.MessageFormat;
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.Collections;
22import java.util.HashMap;
23import java.util.Iterator;
24import java.util.List;
25import java.util.Map;
26
27import org.eclipse.core.runtime.IPath;
28
29import org.eclipse.core.resources.IResource;
30
31import org.eclipse.jface.internal.text.html.HTMLPrinter;
32
33import org.eclipse.jdt.core.IBuffer;
34import org.eclipse.jdt.core.IField;
35import org.eclipse.jdt.core.IJavaProject;
36import org.eclipse.jdt.core.IMember;
37import org.eclipse.jdt.core.IMethod;
38import org.eclipse.jdt.core.IPackageFragmentRoot;
39import org.eclipse.jdt.core.ISourceRange;
40import org.eclipse.jdt.core.IType;
41import org.eclipse.jdt.core.ITypeHierarchy;
42import org.eclipse.jdt.core.JavaCore;
43import org.eclipse.jdt.core.JavaModelException;
44import org.eclipse.jdt.core.Signature;
45import org.eclipse.jdt.core.SourceRange;
46import org.eclipse.jdt.core.dom.ASTNode;
47import org.eclipse.jdt.core.dom.ASTParser;
48import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
49import org.eclipse.jdt.core.dom.CompilationUnit;
50import org.eclipse.jdt.core.dom.IBinding;
51import org.eclipse.jdt.core.dom.IVariableBinding;
52import org.eclipse.jdt.core.dom.Javadoc;
53import org.eclipse.jdt.core.dom.MemberRef;
54import org.eclipse.jdt.core.dom.MethodRef;
55import org.eclipse.jdt.core.dom.MethodRefParameter;
56import org.eclipse.jdt.core.dom.Name;
57import org.eclipse.jdt.core.dom.NodeFinder;
58import org.eclipse.jdt.core.dom.SimpleName;
59import org.eclipse.jdt.core.dom.TagElement;
60import org.eclipse.jdt.core.dom.TextElement;
61
62import org.eclipse.jdt.internal.corext.dom.ASTNodes;
63import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
64import org.eclipse.jdt.internal.corext.util.JdtFlags;
65import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
66import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache;
67
68import org.eclipse.jdt.ui.JavaElementLabels;
69import org.eclipse.jdt.ui.JavaUI;
70import org.eclipse.jdt.ui.JavadocContentAccess;
71import org.eclipse.jdt.ui.SharedASTProvider;
72
73import org.eclipse.jdt.internal.ui.JavaPlugin;
74import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
75import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks;
76
77
78/**
79 * Helper to get the content of a Javadoc comment as HTML.
80 *
81 * <p>
82 * <strong>This is work in progress. Parts of this will later become
83 * API through {@link JavadocContentAccess}</strong>
84 * </p>
85 *
86 * @since 3.4
87 */
88public class JavadocContentAccess2 {
89
90 private static final String BLOCK_TAG_START= "<dl>"; //$NON-NLS-1$
91 private static final String BLOCK_TAG_END= "</dl>"; //$NON-NLS-1$
92
93 private static final String BlOCK_TAG_ENTRY_START= "<dd>"; //$NON-NLS-1$
94 private static final String BlOCK_TAG_ENTRY_END= "</dd>"; //$NON-NLS-1$
95
96 private static final String PARAM_NAME_START= "<b>"; //$NON-NLS-1$
97 private static final String PARAM_NAME_END= "</b> "; //$NON-NLS-1$
98
99 /**
100 * Implements the "Algorithm for Inheriting Method Comments" as specified for <a href=
101 * "http://download.oracle.com/javase/1.4.2/docs/tooldocs/solaris/javadoc.html#inheritingcomments"
102 * >1.4.2</a>, <a href=
103 * "http://download.oracle.com/javase/1.5.0/docs/tooldocs/windows/javadoc.html#inheritingcomments"
104 * >1.5</a>, and <a href=
105 * "http://download.oracle.com/javase/6/docs/technotes/tools/windows/javadoc.html#inheritingcomments"
106 * >1.6</a>.
107 *
108 * <p>
109 * Unfortunately, the implementation is broken in Javadoc implementations since 1.5, see <a
110 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6376959">Sun's bug</a>.
111 * </p>
112 *
113 * <p>
114 * We adhere to the spec.
115 * </p>
116 */
117 private static abstract class InheritDocVisitor {
118 public static final Object STOP_BRANCH= new Object() {
119 @Override
120 public String toString() { return "STOP_BRANCH"; } //$NON-NLS-1$
121 };
122 public static final Object CONTINUE= new Object() {
123 @Override
124 public String toString() { return "CONTINUE"; } //$NON-NLS-1$
125 };
126
127 /**
128 * Visits a type and decides how the visitor should proceed.
129 *
130 * @param currType the current type
131 * @return <ul>
132 * <li>{@link #STOP_BRANCH} to indicate that no Javadoc has been found and visiting
133 * super types should stop here</li>
134 * <li>{@link #CONTINUE} to indicate that no Javadoc has been found and visiting
135 * super types should continue</li>
136 * <li>an {@link Object} or <code>null</code>, to indicate that visiting should be
137 * cancelled immediately. The returned value is the result of
138 * {@link #visitInheritDoc(IType, ITypeHierarchy)}</li>
139 * </ul>
140 * @throws JavaModelException unexpected problem
141 * @see #visitInheritDoc(IType, ITypeHierarchy)
142 */
143 public abstract Object visit(IType currType) throws JavaModelException;
144
145 /**
146 * Visits the super types of the given <code>currentType</code>.
147 *
148 * @param currentType the starting type
149 * @param typeHierarchy a super type hierarchy that contains <code>currentType</code>
150 * @return the result from a call to {@link #visit(IType)}, or <code>null</code> if none of
151 * the calls returned a result
152 * @throws JavaModelException unexpected problem
153 */
154 public Object visitInheritDoc(IType currentType, ITypeHierarchy typeHierarchy) throws JavaModelException {
155 ArrayList<IType> visited= new ArrayList<IType>();
156 visited.add(currentType);
157 Object result= visitInheritDocInterfaces(visited, currentType, typeHierarchy);
158 if (result != InheritDocVisitor.CONTINUE)
159 return result;
160
161 IType superClass;
162 if (currentType.isInterface())
163 superClass= currentType.getJavaProject().findType("java.lang.Object"); //$NON-NLS-1$
164 else
165 superClass= typeHierarchy.getSuperclass(currentType);
166
167 while (superClass != null && ! visited.contains(superClass)) {
168 result= visit(superClass);
169 if (result == InheritDocVisitor.STOP_BRANCH) {
170 return null;
171 } else if (result == InheritDocVisitor.CONTINUE) {
172 visited.add(superClass);
173 result= visitInheritDocInterfaces(visited, superClass, typeHierarchy);
174 if (result != InheritDocVisitor.CONTINUE)
175 return result;
176 else
177 superClass= typeHierarchy.getSuperclass(superClass);
178 } else {
179 return result;
180 }
181 }
182
183 return null;
184 }
185
186 /**
187 * Visits the super interfaces of the given type in the given hierarchy, thereby skipping already visited types.
188 *
189 * @param visited set of visited types
190 * @param currentType type whose super interfaces should be visited
191 * @param typeHierarchy type hierarchy (must include <code>currentType</code>)
192 * @return the result, or {@link #CONTINUE} if no result has been found
193 * @throws JavaModelException unexpected problem
194 */
195 private Object visitInheritDocInterfaces(ArrayList<IType> visited, IType currentType, ITypeHierarchy typeHierarchy) throws JavaModelException {
196 ArrayList<IType> toVisitChildren= new ArrayList<IType>();
197 IType[] superInterfaces= typeHierarchy.getSuperInterfaces(currentType);
198 for (int i= 0; i < superInterfaces.length; i++) {
199 IType superInterface= superInterfaces[i];
200 if (visited.contains(superInterface))
201 continue;
202 visited.add(superInterface);
203 Object result= visit(superInterface);
204 if (result == InheritDocVisitor.STOP_BRANCH) {
205 //skip
206 } else if (result == InheritDocVisitor.CONTINUE) {
207 toVisitChildren.add(superInterface);
208 } else {
209 return result;
210 }
211 }
212 for (Iterator<IType> iter= toVisitChildren.iterator(); iter.hasNext(); ) {
213 IType child= iter.next();
214 Object result= visitInheritDocInterfaces(visited, child, typeHierarchy);
215 if (result != InheritDocVisitor.CONTINUE)
216 return result;
217 }
218 return InheritDocVisitor.CONTINUE;
219 }
220 }
221
222 static class JavadocLookup {
223 private static final JavadocLookup NONE= new JavadocLookup(null) {
224 @Override
225 public CharSequence getInheritedMainDescription(IMethod method) {
226 return null;
227 }
228 @Override
229 public CharSequence getInheritedParamDescription(IMethod method, int i) {
230 return null;
231 }
232 @Override
233 public CharSequence getInheritedReturnDescription(IMethod method) {
234 return null;
235 }
236 @Override
237 public CharSequence getInheritedExceptionDescription(IMethod method, String name) {
238 return null;
239 }
240 };
241
242 private static interface DescriptionGetter {
243 /**
244 * Returns a Javadoc tag description or <code>null</code>.
245 *
246 * @param contentAccess the content access
247 * @return the description, or <code>null</code> if none
248 * @throws JavaModelException unexpected problem
249 */
250 CharSequence getDescription(JavadocContentAccess2 contentAccess) throws JavaModelException;
251 }
252
253 private final IType fStartingType;
254 private final HashMap<IMethod, JavadocContentAccess2> fContentAccesses;
255
256 private ITypeHierarchy fTypeHierarchy;
257 private MethodOverrideTester fOverrideTester;
258
259
260 private JavadocLookup(IType startingType) {
261 fStartingType= startingType;
262 fContentAccesses= new HashMap<IMethod, JavadocContentAccess2>();
263 }
264
265 /**
266 * For the given method, returns the main description from an overridden method.
267 *
268 * @param method a method
269 * @return the description that replaces the <code>{&#64;inheritDoc}</code> tag,
270 * or <code>null</code> if none could be found
271 */
272 public CharSequence getInheritedMainDescription(IMethod method) {
273 return getInheritedDescription(method, new DescriptionGetter() {
274 public CharSequence getDescription(JavadocContentAccess2 contentAccess) {
275 return contentAccess.getMainDescription();
276 }
277 });
278 }
279
280 /**
281 * For the given method, returns the @param tag description for the given parameter
282 * from an overridden method.
283 *
284 * @param method a method
285 * @param paramIndex the index of the parameter
286 * @return the description that replaces the <code>{&#64;inheritDoc}</code> tag,
287 * or <code>null</code> if none could be found
288 */
289 public CharSequence getInheritedParamDescription(IMethod method, final int paramIndex) {
290 return getInheritedDescription(method, new DescriptionGetter() {
291 public CharSequence getDescription(JavadocContentAccess2 contentAccess) throws JavaModelException {
292 return contentAccess.getInheritedParamDescription(paramIndex);
293 }
294 });
295 }
296
297 /**
298 * For the given method, returns the @return tag description from an overridden method.
299 *
300 * @param method a method
301 * @return the description that replaces the <code>{&#64;inheritDoc}</code> tag,
302 * or <code>null</code> if none could be found
303 */
304 public CharSequence getInheritedReturnDescription(IMethod method) {
305 return getInheritedDescription(method, new DescriptionGetter() {
306 public CharSequence getDescription(JavadocContentAccess2 contentAccess) {
307 return contentAccess.getReturnDescription();
308 }
309 });
310 }
311
312 /**
313 * For the given method, returns the @throws/@exception tag description for the given
314 * exception from an overridden method.
315 *
316 * @param method a method
317 * @param simpleName the simple name of an exception
318 * @return the description that replaces the <code>{&#64;inheritDoc}</code> tag,
319 * or <code>null</code> if none could be found
320 */
321 public CharSequence getInheritedExceptionDescription(IMethod method, final String simpleName) {
322 return getInheritedDescription(method, new DescriptionGetter() {
323 public CharSequence getDescription(JavadocContentAccess2 contentAccess) {
324 return contentAccess.getExceptionDescription(simpleName);
325 }
326 });
327 }
328
329 private CharSequence getInheritedDescription(final IMethod method, final DescriptionGetter descriptionGetter) {
330 try {
331 return (CharSequence) new InheritDocVisitor() {
332 @Override
333 public Object visit(IType currType) throws JavaModelException {
334 IMethod overridden= getOverrideTester().findOverriddenMethodInType(currType, method);
335 if (overridden == null)
336 return InheritDocVisitor.CONTINUE;
337
338 JavadocContentAccess2 contentAccess= getJavadocContentAccess(overridden);
339 if (contentAccess == null) {
340 if (overridden.getOpenable().getBuffer() == null) {
341 // Don't continue this branch when no source is available.
342 // We don't extract individual tags from Javadoc attachments,
343 // and it would be wrong to copy doc from further up the branch,
344 // thereby skipping doc from this overridden method.
345 return InheritDocVisitor.STOP_BRANCH;
346 } else {
347 return InheritDocVisitor.CONTINUE;
348 }
349 }
350
351 CharSequence overriddenDescription= descriptionGetter.getDescription(contentAccess);
352 if (overriddenDescription != null)
353 return overriddenDescription;
354 else
355 return InheritDocVisitor.CONTINUE;
356 }
357 }.visitInheritDoc(method.getDeclaringType(), getTypeHierarchy());
358 } catch (JavaModelException e) {
359 JavaPlugin.log(e);
360 }
361 return null;
362 }
363
364 /**
365 * @param method the method
366 * @return the Javadoc content access for the given method, or
367 * <code>null</code> if no Javadoc could be found in source
368 * @throws JavaModelException unexpected problem
369 */
370 private JavadocContentAccess2 getJavadocContentAccess(IMethod method) throws JavaModelException {
371 Object cached= fContentAccesses.get(method);
372 if (cached != null)
373 return (JavadocContentAccess2) cached;
374 if (fContentAccesses.containsKey(method))
375 return null;
376
377 IBuffer buf= method.getOpenable().getBuffer();
378 if (buf == null) { // no source attachment found
379 fContentAccesses.put(method, null);
380 return null;
381 }
382
383 ISourceRange javadocRange= method.getJavadocRange();
384 if (javadocRange == null) {
385 fContentAccesses.put(method, null);
386 return null;
387 }
388
389 String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength());
390 Javadoc javadoc= getJavadocNode(method, rawJavadoc);
391 if (javadoc == null) {
392 fContentAccesses.put(method, null);
393 return null;
394 }
395
396 JavadocContentAccess2 contentAccess= new JavadocContentAccess2(method, javadoc, rawJavadoc, this);
397 return contentAccess.generated_4672312213436743719(method, JavadocLookup.this);
398 }
399
400 private ITypeHierarchy getTypeHierarchy() throws JavaModelException {
401 if (fTypeHierarchy == null)
402 fTypeHierarchy= SuperTypeHierarchyCache.getTypeHierarchy(fStartingType);
403 return fTypeHierarchy;
404 }
405
406 private MethodOverrideTester getOverrideTester() throws JavaModelException {
407 if (fOverrideTester == null)
408 fOverrideTester= SuperTypeHierarchyCache.getMethodOverrideTester(fStartingType);
409 return fOverrideTester;
410 }
411
412 public String generated_7350347271025408250(JavadocContentAccess2 javadoccontentaccess2, List<String> parameterNames, List<String> exceptionNames, TagElement deprecatedTag, TagElement start) {
413 List<TagElement> parameters= new ArrayList<TagElement>();
414 TagElement returnTag= null;
415 List<TagElement> exceptions= new ArrayList<TagElement>();
416 List<TagElement> versions= new ArrayList<TagElement>();
417 List<TagElement> authors= new ArrayList<TagElement>();
418 List<TagElement> sees= new ArrayList<TagElement>();
419 List<TagElement> since= new ArrayList<TagElement>();
420 List<TagElement> rest= new ArrayList<TagElement>();
421
422 List<TagElement> tags= javadoccontentaccess2.fJavadoc.tags();
423 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
424 TagElement tag= iter.next();
425 String tagName= tag.getTagName();
426 if (tagName == null) {
427 start= tag;
428
429 } else if (TagElement.TAG_PARAM.equals(tagName)) {
430 parameters.add(tag);
431 List<? extends ASTNode> fragments= tag.fragments();
432 if (fragments.size() > 0) {
433 Object first= fragments.get(0);
434 if (first instanceof SimpleName) {
435 String name= ((SimpleName) first).getIdentifier();
436 int paramIndex= parameterNames.indexOf(name);
437 if (paramIndex != -1) {
438 parameterNames.set(paramIndex, null);
439 }
440 }
441 }
442
443 } else if (TagElement.TAG_RETURN.equals(tagName)) {
444 if (returnTag == null)
445 returnTag= tag; // the Javadoc tool only shows the first return tag
446
447 } else if (TagElement.TAG_EXCEPTION.equals(tagName) || TagElement.TAG_THROWS.equals(tagName)) {
448 exceptions.add(tag);
449 List<? extends ASTNode> fragments= tag.fragments();
450 if (fragments.size() > 0) {
451 Object first= fragments.get(0);
452 if (first instanceof Name) {
453 String name= ASTNodes.getSimpleNameIdentifier((Name) first);
454 int exceptionIndex= exceptionNames.indexOf(name);
455 if (exceptionIndex != -1) {
456 exceptionNames.set(exceptionIndex, null);
457 }
458 }
459 }
460
461 } else if (TagElement.TAG_SINCE.equals(tagName)) {
462 since.add(tag);
463 } else if (TagElement.TAG_VERSION.equals(tagName)) {
464 versions.add(tag);
465 } else if (TagElement.TAG_AUTHOR.equals(tagName)) {
466 authors.add(tag);
467 } else if (TagElement.TAG_SEE.equals(tagName)) {
468 sees.add(tag);
469 } else if (TagElement.TAG_DEPRECATED.equals(tagName)) {
470 if (deprecatedTag == null)
471 deprecatedTag= tag; // the Javadoc tool only shows the first deprecated tag
472 } else {
473 rest.add(tag);
474 }
475 }
476
477 //TODO: @Documented annotations before header
478 if (deprecatedTag != null)
479 javadoccontentaccess2.handleDeprecatedTag(deprecatedTag);
480 if (start != null)
481 javadoccontentaccess2.handleContentElements(start.fragments());
482 else if (javadoccontentaccess2.fMethod != null) {
483 CharSequence inherited= getInheritedMainDescription(javadoccontentaccess2.fMethod);
484 // The Javadoc tool adds "Description copied from class: ..." (only for the main description).
485 // We don't bother doing that.
486 javadoccontentaccess2.handleInherited(inherited);
487 }
488
489 CharSequence[] parameterDescriptions= new CharSequence[parameterNames.size()];
490 boolean hasInheritedParameters= javadoccontentaccess2.inheritParameterDescriptions(parameterNames, parameterDescriptions);
491 boolean hasParameters= parameters.size() > 0 || hasInheritedParameters;
492
493 CharSequence returnDescription= null;
494 if (returnTag == null && javadoccontentaccess2.needsReturnTag())
495 returnDescription= getInheritedReturnDescription(javadoccontentaccess2.fMethod);
496 boolean hasReturnTag= returnTag != null || returnDescription != null;
497
498 CharSequence[] exceptionDescriptions= new CharSequence[exceptionNames.size()];
499 boolean hasInheritedExceptions= javadoccontentaccess2.inheritExceptionDescriptions(exceptionNames, exceptionDescriptions);
500 boolean hasExceptions= exceptions.size() > 0 || hasInheritedExceptions;
501
502 if (hasParameters || hasReturnTag || hasExceptions
503 || versions.size() > 0 || authors.size() > 0 || since.size() > 0 || sees.size() > 0 || rest.size() > 0
504 || (javadoccontentaccess2.fBuf.length() > 0 && (parameterDescriptions.length > 0 || exceptionDescriptions.length > 0))
505 ) {
506 javadoccontentaccess2.handleSuperMethodReferences();
507 javadoccontentaccess2.fBuf.append(JavadocContentAccess2.BLOCK_TAG_START);
508 javadoccontentaccess2.handleParameterTags(parameters, parameterNames, parameterDescriptions);
509 javadoccontentaccess2.handleReturnTag(returnTag, returnDescription);
510 javadoccontentaccess2.handleExceptionTags(exceptions, exceptionNames, exceptionDescriptions);
511 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_since_section, since);
512 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_version_section, versions);
513 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_author_section, authors);
514 javadoccontentaccess2.handleBlockTags(JavaDocMessages.JavaDoc2HTMLTextReader_see_section, sees);
515 javadoccontentaccess2.handleBlockTags(rest);
516 javadoccontentaccess2.fBuf.append(JavadocContentAccess2.BLOCK_TAG_END);
517
518 } else if (javadoccontentaccess2.fBuf.length() > 0) {
519 javadoccontentaccess2.handleSuperMethodReferences();
520 }
521
522 String result= javadoccontentaccess2.fBuf.toString();
523 javadoccontentaccess2.fBuf= null;
524 return result;
525 }
526
527 public boolean generated_8554163332790850784(JavadocContentAccess2 javadoccontentaccess2, TagElement node) {
528 try {
529 if (javadoccontentaccess2.fMethod == null)
530 return false;
531
532 TagElement blockTag= (TagElement) node.getParent();
533 String blockTagName= blockTag.getTagName();
534
535 if (blockTagName == null) {
536 CharSequence inherited= getInheritedMainDescription(javadoccontentaccess2.fMethod);
537 return javadoccontentaccess2.handleInherited(inherited);
538
539 } else if (TagElement.TAG_PARAM.equals(blockTagName)) {
540 List<? extends ASTNode> fragments= blockTag.fragments();
541 if (fragments.size() > 0) {
542 Object first= fragments.get(0);
543 if (first instanceof SimpleName) {
544 String name= ((SimpleName) first).getIdentifier();
545 String[] parameterNames= javadoccontentaccess2.fMethod.getParameterNames();
546 for (int i= 0; i < parameterNames.length; i++) {
547 if (name.equals(parameterNames[i])) {
548 CharSequence inherited= getInheritedParamDescription(javadoccontentaccess2.fMethod, i);
549 return javadoccontentaccess2.handleInherited(inherited);
550 }
551 }
552 }
553 }
554
555 } else if (TagElement.TAG_RETURN.equals(blockTagName)) {
556 CharSequence inherited= getInheritedReturnDescription(javadoccontentaccess2.fMethod);
557 return javadoccontentaccess2.handleInherited(inherited);
558
559 } else if (TagElement.TAG_THROWS.equals(blockTagName) || TagElement.TAG_EXCEPTION.equals(blockTagName)) {
560 List<? extends ASTNode> fragments= blockTag.fragments();
561 if (fragments.size() > 0) {
562 Object first= fragments.get(0);
563 if (first instanceof Name) {
564 String name= ASTNodes.getSimpleNameIdentifier((Name) first);
565 CharSequence inherited= getInheritedExceptionDescription(javadoccontentaccess2.fMethod, name);
566 return javadoccontentaccess2.handleInherited(inherited);
567 }
568 }
569 }
570 } catch (JavaModelException e) {
571 JavaPlugin.log(e);
572 }
573 return false;
574 }
575 }
576
577 private final IMember fMember;
578 /**
579 * The method, or <code>null</code> if {@link #fMember} is not a method where @inheritDoc could work.
580 */
581 private final IMethod fMethod;
582 private final Javadoc fJavadoc;
583 private final String fSource;
584 private final JavadocLookup fJavadocLookup;
585
586 private StringBuffer fBuf;
587 private int fLiteralContent;
588 private StringBuffer fMainDescription;
589 private StringBuffer fReturnDescription;
590 private StringBuffer[] fParamDescriptions;
591 private HashMap<String, StringBuffer> fExceptionDescriptions;
592
593 private JavadocContentAccess2(IMethod method, Javadoc javadoc, String source, JavadocLookup lookup) {
594 fMember= method;
595 fMethod= method;
596 fJavadoc= javadoc;
597 fSource= source;
598 fJavadocLookup= lookup;
599 }
600
601 private JavadocContentAccess2(IMember member, Javadoc javadoc, String source) {
602 fMember= member;
603 fMethod= null;
604 fJavadoc= javadoc;
605 fSource= source;
606 fJavadocLookup= JavadocLookup.NONE;
607 }
608
609 /**
610 * Gets an IMember's Javadoc comment content from the source or Javadoc attachment
611 * and renders the tags and links in HTML.
612 * Returns <code>null</code> if the member does not contain a Javadoc comment or if no source is available.
613 *
614 * @param member the member to get the Javadoc of
615 * @param useAttachedJavadoc if <code>true</code> Javadoc will be extracted from attached Javadoc
616 * if there's no source
617 * @return the Javadoc comment content in HTML or <code>null</code> if the member
618 * does not have a Javadoc comment or if no source is available
619 * @throws JavaModelException is thrown when the element's Javadoc can not be accessed
620 */
621 public static String getHTMLContent(IMember member, boolean useAttachedJavadoc) throws JavaModelException {
622 String sourceJavadoc= getHTMLContentFromSource(member);
623 if (sourceJavadoc == null || sourceJavadoc.length() == 0 || sourceJavadoc.trim().equals("{@inheritDoc}")) { //$NON-NLS-1$
624 if (useAttachedJavadoc) {
625 if (member.getOpenable().getBuffer() == null) { // only if no source available
626 return member.getAttachedJavadoc(null);
627 }
628 if (canInheritJavadoc(member)) {
629 IMethod method= (IMethod) member;
630 String attachedDocInHierarchy= findAttachedDocInHierarchy(method);
631
632 // Prepend "Overrides:" / "Specified by:" reference headers to make clear
633 // that description has been copied from super method.
634 if (attachedDocInHierarchy == null)
635 return sourceJavadoc;
636 StringBuffer superMethodReferences= createSuperMethodReferences(method);
637 if (superMethodReferences == null)
638 return attachedDocInHierarchy;
639 superMethodReferences.append(attachedDocInHierarchy);
640 return superMethodReferences.toString();
641 }
642 }
643 }
644 return sourceJavadoc;
645 }
646
647 private static StringBuffer createSuperMethodReferences(final IMethod method) throws JavaModelException {
648 IType type= method.getDeclaringType();
649 ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(type);
650 final MethodOverrideTester tester= SuperTypeHierarchyCache.getMethodOverrideTester(type);
651
652 final ArrayList<IMethod> superInterfaceMethods= new ArrayList<IMethod>();
653 final IMethod[] superClassMethod= { null };
654 new InheritDocVisitor() {
655 @Override
656 public Object visit(IType currType) throws JavaModelException {
657 IMethod overridden= tester.findOverriddenMethodInType(currType, method);
658 if (overridden == null)
659 return InheritDocVisitor.CONTINUE;
660
661 if (currType.isInterface())
662 superInterfaceMethods.add(overridden);
663 else
664 superClassMethod[0]= overridden;
665
666 return STOP_BRANCH;
667 }
668 }.visitInheritDoc(type, hierarchy);
669
670 boolean hasSuperInterfaceMethods= superInterfaceMethods.size() != 0;
671 if (!hasSuperInterfaceMethods && superClassMethod[0] == null)
672 return null;
673
674 StringBuffer buf= new StringBuffer();
675 buf.append("<div>"); //$NON-NLS-1$
676 if (hasSuperInterfaceMethods) {
677 buf.append("<b>"); //$NON-NLS-1$
678 buf.append(JavaDocMessages.JavaDoc2HTMLTextReader_specified_by_section);
679 buf.append("</b> "); //$NON-NLS-1$
680 for (Iterator<IMethod> iter= superInterfaceMethods.iterator(); iter.hasNext(); ) {
681 IMethod overridden= iter.next();
682 buf.append(createMethodInTypeLinks(overridden));
683 if (iter.hasNext())
684 buf.append(JavaElementLabels.COMMA_STRING);
685 }
686 }
687 if (superClassMethod[0] != null) {
688 if (hasSuperInterfaceMethods)
689 buf.append(JavaElementLabels.COMMA_STRING);
690 buf.append("<b>"); //$NON-NLS-1$
691 buf.append(JavaDocMessages.JavaDoc2HTMLTextReader_overrides_section);
692 buf.append("</b> "); //$NON-NLS-1$
693 buf.append(createMethodInTypeLinks(superClassMethod[0]));
694 }
695 buf.append("</div>"); //$NON-NLS-1$
696 return buf;
697 }
698
699 private static String createMethodInTypeLinks(IMethod overridden) {
700 CharSequence methodLink= createSimpleMemberLink(overridden);
701 CharSequence typeLink= createSimpleMemberLink(overridden.getDeclaringType());
702 String methodInType= MessageFormat.format(JavaDocMessages.JavaDoc2HTMLTextReader_method_in_type, new Object[] { methodLink, typeLink });
703 return methodInType;
704 }
705
706 private static CharSequence createSimpleMemberLink(IMember member) {
707 StringBuffer buf= new StringBuffer();
708 buf.append("<a href='"); //$NON-NLS-1$
709 try {
710 String uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, member);
711 buf.append(uri);
712 } catch (URISyntaxException e) {
713 JavaPlugin.log(e);
714 }
715 buf.append("'>"); //$NON-NLS-1$
716 JavaElementLabels.getElementLabel(member, 0, buf);
717 buf.append("</a>"); //$NON-NLS-1$
718 return buf;
719 }
720
721 private static String getHTMLContentFromSource(IMember member) throws JavaModelException {
722 IBuffer buf= member.getOpenable().getBuffer();
723 if (buf == null) {
724 return null; // no source attachment found
725 }
726
727 ISourceRange javadocRange= member.getJavadocRange();
728 if (javadocRange == null) {
729 if (canInheritJavadoc(member)) {
730 // Try to use the inheritDoc algorithm. If it finds nothing (in source), return null.
731 String inheritedJavadoc= javadoc2HTML(member, "/***/"); //$NON-NLS-1$
732 return inheritedJavadoc != null && inheritedJavadoc.length() > 0 ? inheritedJavadoc : null;
733 } else {
734 return null;
735 }
736 }
737
738 String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength());
739 return javadoc2HTML(member, rawJavadoc);
740 }
741
742 private static Javadoc getJavadocNode(IMember member, String rawJavadoc) {
743 //FIXME: take from SharedASTProvider if available
744 //Caveat: Javadoc nodes are not available when Javadoc processing has been disabled!
745 //https://bugs.eclipse.org/bugs/show_bug.cgi?id=212207
746
747 ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
748
749 IJavaProject javaProject= member.getJavaProject();
750 parser.setProject(javaProject);
751 Map<String, String> options= javaProject.getOptions(true);
752 options.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212207
753 parser.setCompilerOptions(options);
754
755 String source= rawJavadoc + "class C{}"; //$NON-NLS-1$
756 parser.setSource(source.toCharArray());
757
758 CompilationUnit root= (CompilationUnit) parser.createAST(null);
759 if (root == null)
760 return null;
761 List<AbstractTypeDeclaration> types= root.types();
762 if (types.size() != 1)
763 return null;
764 AbstractTypeDeclaration type= types.get(0);
765 return type.getJavadoc();
766 }
767
768 private static String javadoc2HTML(IMember member, String rawJavadoc) {
769 Javadoc javadoc= getJavadocNode(member, rawJavadoc);
770
771 if (javadoc == null) {
772 // fall back to JavadocContentAccess:
773 try {
774 Reader contentReader= JavadocContentAccess.getHTMLContentReader(member, false, false);
775 if (contentReader != null)
776 return getString(contentReader);
777 } catch (JavaModelException e) {
778 JavaPlugin.log(e);
779 }
780 return null;
781 }
782
783 if (canInheritJavadoc(member)) {
784 IMethod method= (IMethod) member;
785 return new JavadocContentAccess2(method, javadoc, rawJavadoc, new JavadocLookup(method.getDeclaringType())).toHTML();
786 }
787 return new JavadocContentAccess2(member, javadoc, rawJavadoc).toHTML();
788 }
789
790 private static boolean canInheritJavadoc(IMember member) {
791 if (member instanceof IMethod && member.getJavaProject().exists()) {
792 /*
793 * Exists test catches ExternalJavaProject, in which case no hierarchy can be built.
794 */
795 try {
796 return ! ((IMethod) member).isConstructor();
797 } catch (JavaModelException e) {
798 JavaPlugin.log(e);
799 }
800 }
801 return false;
802 }
803
804 /**
805 * Gets the reader content as a String
806 *
807 * @param reader the reader
808 * @return the reader content as string
809 */
810 private static String getString(Reader reader) {
811 StringBuffer buf= new StringBuffer();
812 char[] buffer= new char[1024];
813 int count;
814 try {
815 while ((count= reader.read(buffer)) != -1)
816 buf.append(buffer, 0, count);
817 } catch (IOException e) {
818 return null;
819 }
820 return buf.toString();
821 }
822
823 /**
824 * Finds the first available attached Javadoc in the hierarchy of the given method.
825 *
826 * @param method the method
827 * @return the inherited Javadoc from the Javadoc attachment, or <code>null</code> if none
828 * @throws JavaModelException unexpected problem
829 */
830 private static String findAttachedDocInHierarchy(final IMethod method) throws JavaModelException {
831 IType type= method.getDeclaringType();
832 ITypeHierarchy hierarchy= SuperTypeHierarchyCache.getTypeHierarchy(type);
833 final MethodOverrideTester tester= SuperTypeHierarchyCache.getMethodOverrideTester(type);
834
835 return (String) new InheritDocVisitor() {
836 @Override
837 public Object visit(IType currType) throws JavaModelException {
838 IMethod overridden= tester.findOverriddenMethodInType(currType, method);
839 if (overridden == null)
840 return InheritDocVisitor.CONTINUE;
841
842 if (overridden.getOpenable().getBuffer() == null) { // only if no source available
843 //TODO: BaseURL for method can be wrong for attached Javadoc from overridden
844 // (e.g. when overridden is from rt.jar). Fix would be to add baseURL here.
845 String attachedJavadoc= overridden.getAttachedJavadoc(null);
846 if (attachedJavadoc != null)
847 return attachedJavadoc;
848 }
849 return CONTINUE;
850 }
851 }.visitInheritDoc(type, hierarchy);
852 }
853
854 private String toHTML() {
855 fBuf= new StringBuffer();
856 fLiteralContent= 0;
857
858 // After first loop, non-null entries in the following two lists are missing and need to be inherited:
859 List<String> parameterNames= initParameterNames();
860 List<String> exceptionNames= initExceptionNames();
861
862 TagElement deprecatedTag= null;
863 TagElement start= null;
864 return fJavadocLookup.generated_7350347271025408250(this, parameterNames, exceptionNames, deprecatedTag, start);
865 }
866
867 private void handleDeprecatedTag(TagElement tag) {
868 fBuf.append("<p><b>"); //$NON-NLS-1$
869 fBuf.append(JavaDocMessages.JavaDoc2HTMLTextReader_deprecated_section);
870 fBuf.append("</b> <i>"); //$NON-NLS-1$
871 handleContentElements(tag.fragments());
872 fBuf.append("</i><p>"); //$NON-NLS-1$ TODO: Why not </p>? See https://bugs.eclipse.org/bugs/show_bug.cgi?id=243318 .
873 }
874
875 private void handleSuperMethodReferences() {
876 if (fMethod != null) {
877 try {
878 StringBuffer superMethodReferences= createSuperMethodReferences(fMethod);
879 if (superMethodReferences != null)
880 fBuf.append(superMethodReferences);
881 } catch (JavaModelException e) {
882 JavaPlugin.log(e);
883 }
884 }
885 }
886
887 private List<String> initParameterNames() {
888 if (fMethod != null) {
889 try {
890 return new ArrayList<String>(Arrays.asList(fMethod.getParameterNames()));
891 } catch (JavaModelException e) {
892 JavaPlugin.log(e);
893 }
894 }
895 return Collections.emptyList();
896 }
897
898 private List<String> initExceptionNames() {
899 if (fMethod != null) {
900 try {
901 String[] exceptionTypes= fMethod.getExceptionTypes();
902 ArrayList<String> exceptionNames= new ArrayList<String>();
903 for (int i= 0; i < exceptionTypes.length; i++) {
904 exceptionNames.add(Signature.getSimpleName(Signature.toString(exceptionTypes[i])));
905 }
906 return exceptionNames;
907 } catch (JavaModelException e) {
908 JavaPlugin.log(e);
909 }
910 }
911 return Collections.emptyList();
912 }
913
914 private boolean needsReturnTag() {
915 if (fMethod == null)
916 return false;
917 try {
918 return ! Signature.SIG_VOID.equals(fMethod.getReturnType());
919 } catch (JavaModelException e) {
920 JavaPlugin.log(e);
921 return false;
922 }
923 }
924
925 private boolean inheritParameterDescriptions(List<String> parameterNames, CharSequence[] parameterDescriptions) {
926 boolean hasInheritedParameters= false;
927 for (int i= 0; i < parameterNames.size(); i++) {
928 String name= parameterNames.get(i);
929 if (name != null) {
930 parameterDescriptions[i]= fJavadocLookup.getInheritedParamDescription(fMethod, i);
931 if (parameterDescriptions[i] != null)
932 hasInheritedParameters= true;
933 }
934 }
935 return hasInheritedParameters;
936 }
937
938 private boolean inheritExceptionDescriptions(List<String> exceptionNames, CharSequence[] exceptionDescriptions) {
939 boolean hasInheritedExceptions= false;
940 for (int i= 0; i < exceptionNames.size(); i++) {
941 String name= exceptionNames.get(i);
942 if (name != null) {
943 exceptionDescriptions[i]= fJavadocLookup.getInheritedExceptionDescription(fMethod, name);
944 if (exceptionDescriptions[i] != null)
945 hasInheritedExceptions= true;
946 }
947 }
948 return hasInheritedExceptions;
949 }
950
951 CharSequence getMainDescription() {
952 if (fMainDescription == null) {
953 fMainDescription= new StringBuffer();
954 fBuf= fMainDescription;
955 fLiteralContent= 0;
956
957 List<TagElement> tags= fJavadoc.tags();
958 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
959 TagElement tag= iter.next();
960 String tagName= tag.getTagName();
961 if (tagName == null) {
962 handleContentElements(tag.fragments());
963 break;
964 }
965 }
966
967 fBuf= null;
968 }
969 return fMainDescription.length() > 0 ? fMainDescription : null;
970 }
971
972 CharSequence getReturnDescription() {
973 if (fReturnDescription == null) {
974 fReturnDescription= new StringBuffer();
975 fBuf= fReturnDescription;
976 fLiteralContent= 0;
977
978 List<TagElement> tags= fJavadoc.tags();
979 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
980 TagElement tag= iter.next();
981 String tagName= tag.getTagName();
982 if (TagElement.TAG_RETURN.equals(tagName)) {
983 handleContentElements(tag.fragments());
984 break;
985 }
986 }
987
988 fBuf= null;
989 }
990 return fReturnDescription.length() > 0 ? fReturnDescription : null;
991 }
992
993 CharSequence getInheritedParamDescription(int paramIndex) throws JavaModelException {
994 if (fMethod != null) {
995 String[] parameterNames= fMethod.getParameterNames();
996 if (fParamDescriptions == null) {
997 fParamDescriptions= new StringBuffer[parameterNames.length];
998 } else {
999 StringBuffer description= fParamDescriptions[paramIndex];
1000 if (description != null) {
1001 return description.length() > 0 ? description : null;
1002 }
1003 }
1004
1005 StringBuffer description= new StringBuffer();
1006 fParamDescriptions[paramIndex]= description;
1007 fBuf= description;
1008 fLiteralContent= 0;
1009
1010 String paramName= parameterNames[paramIndex];
1011 List<TagElement> tags= fJavadoc.tags();
1012 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1013 TagElement tag= iter.next();
1014 String tagName= tag.getTagName();
1015 if (TagElement.TAG_PARAM.equals(tagName)) {
1016 List<? extends ASTNode> fragments= tag.fragments();
1017 if (fragments.size() > 0) {
1018 Object first= fragments.get(0);
1019 if (first instanceof SimpleName) {
1020 String name= ((SimpleName) first).getIdentifier();
1021 if (name.equals(paramName)) {
1022 handleContentElements(fragments.subList(1, fragments.size()));
1023 break;
1024 }
1025 }
1026 }
1027 }
1028 }
1029
1030 fBuf= null;
1031 return description.length() > 0 ? description : null;
1032 }
1033 return null;
1034 }
1035
1036 CharSequence getExceptionDescription(String simpleName) {
1037 if (fMethod != null) {
1038 if (fExceptionDescriptions == null) {
1039 fExceptionDescriptions= new HashMap<String, StringBuffer>();
1040 } else {
1041 StringBuffer description= fExceptionDescriptions.get(simpleName);
1042 if (description != null) {
1043 return description.length() > 0 ? description : null;
1044 }
1045 }
1046
1047 StringBuffer description= new StringBuffer();
1048 fExceptionDescriptions.put(simpleName, description);
1049 fBuf= description;
1050 fLiteralContent= 0;
1051
1052 List<TagElement> tags= fJavadoc.tags();
1053 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1054 TagElement tag= iter.next();
1055 String tagName= tag.getTagName();
1056 if (TagElement.TAG_THROWS.equals(tagName) || TagElement.TAG_EXCEPTION.equals(tagName)) {
1057 List<? extends ASTNode> fragments= tag.fragments();
1058 if (fragments.size() > 0) {
1059 Object first= fragments.get(0);
1060 if (first instanceof Name) {
1061 String name= ASTNodes.getSimpleNameIdentifier((Name) first);
1062 if (name.equals(simpleName)) {
1063 if (fragments.size() > 1)
1064 handleContentElements(fragments.subList(1, fragments.size()));
1065 break;
1066 }
1067 }
1068 }
1069 }
1070 }
1071
1072 fBuf= null;
1073 return description.length() > 0 ? description : null;
1074 }
1075 return null;
1076 }
1077
1078
1079 private void handleContentElements(List<? extends ASTNode> nodes) {
1080 handleContentElements(nodes, false);
1081 }
1082
1083 private void handleContentElements(List<? extends ASTNode> nodes, boolean skipLeadingWhitespace) {
1084 ASTNode previousNode= null;
1085 for (Iterator<? extends ASTNode> iter= nodes.iterator(); iter.hasNext(); ) {
1086 ASTNode child= iter.next();
1087 if (previousNode != null) {
1088 int previousEnd= previousNode.getStartPosition() + previousNode.getLength();
1089 int childStart= child.getStartPosition();
1090 if (previousEnd > childStart) {
1091 // should never happen, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=304826
1092 Exception exception= new Exception("Illegal ASTNode positions: previousEnd=" + previousEnd //$NON-NLS-1$
1093 + ", childStart=" + childStart //$NON-NLS-1$
1094 + ", member=" + fMember.getHandleIdentifier() //$NON-NLS-1$
1095 + ", Javadoc:\n" + fSource); //$NON-NLS-1$
1096 JavaPlugin.log(exception);
1097 } else if (previousEnd != childStart) {
1098 // Need to preserve whitespace before a node that's not
1099 // directly following the previous node (e.g. on a new line)
1100 // due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=206518 :
1101 String textWithStars= fSource.substring(previousEnd, childStart);
1102 String text= removeDocLineIntros(textWithStars);
1103 fBuf.append(text);
1104 }
1105 }
1106 previousNode= child;
1107 if (child instanceof TextElement) {
1108 String text= ((TextElement) child).getText();
1109 if (skipLeadingWhitespace) {
1110 text= text.replaceFirst("^\\s+", ""); //$NON-NLS-1$ //$NON-NLS-2$
1111 }
1112 handleText(text);
1113 } else if (child instanceof TagElement) {
1114 handleInlineTagElement((TagElement) child);
1115 } else {
1116 // This is unexpected. Fail gracefully by just copying the source.
1117 int start= child.getStartPosition();
1118 String text= fSource.substring(start, start + child.getLength());
1119 fBuf.append(removeDocLineIntros(text));
1120 }
1121 }
1122 }
1123
1124 private String removeDocLineIntros(String textWithStars) {
1125 String lineBreakGroup= "(\\r\\n?|\\n)"; //$NON-NLS-1$
1126 String noBreakSpace= "[^\r\n&&\\s]"; //$NON-NLS-1$
1127 return textWithStars.replaceAll(lineBreakGroup + noBreakSpace + "*\\*" /*+ noBreakSpace + '?'*/, "$1"); //$NON-NLS-1$ //$NON-NLS-2$
1128 }
1129
1130 private void handleText(String text) {
1131 if (fLiteralContent == 0) {
1132 fBuf.append(text);
1133 } else {
1134 appendEscaped(fBuf, text);
1135 }
1136 }
1137
1138 private static void appendEscaped(StringBuffer buf, String text) {
1139 int nextToCopy= 0;
1140 int length= text.length();
1141 for (int i= 0; i < length; i++) {
1142 char ch= text.charAt(i);
1143 String rep= null;
1144 switch (ch) {
1145 case '&':
1146 rep= "&amp;"; //$NON-NLS-1$
1147 break;
1148 case '"':
1149 rep= "&quot;"; //$NON-NLS-1$
1150 break;
1151 case '<':
1152 rep= "&lt;"; //$NON-NLS-1$
1153 break;
1154 case '>':
1155 rep= "&gt;"; //$NON-NLS-1$
1156 break;
1157 }
1158 if (rep != null) {
1159 if (nextToCopy < i)
1160 buf.append(text.substring(nextToCopy, i));
1161 buf.append(rep);
1162 nextToCopy= i + 1;
1163 }
1164 }
1165 if (nextToCopy < length)
1166 buf.append(text.substring(nextToCopy));
1167 }
1168
1169 private void handleInlineTagElement(TagElement node) {
1170 String name= node.getTagName();
1171
1172 if (TagElement.TAG_VALUE.equals(name) && handleValueTag(node))
1173 return;
1174
1175 boolean isLink= TagElement.TAG_LINK.equals(name);
1176 boolean isLinkplain= TagElement.TAG_LINKPLAIN.equals(name);
1177 boolean isCode= TagElement.TAG_CODE.equals(name);
1178 boolean isLiteral= TagElement.TAG_LITERAL.equals(name);
1179
1180 if (isLiteral || isCode)
1181 fLiteralContent++;
1182 if (isLink || isCode)
1183 fBuf.append("<code>"); //$NON-NLS-1$
1184
1185 if (isLink || isLinkplain)
1186 handleLink(node.fragments());
1187 else if (isCode || isLiteral)
1188 handleContentElements(node.fragments(), true);
1189 else if (handleInheritDoc(node)) {
1190 // handled
1191 } else if (handleDocRoot(node)) {
1192 // handled
1193 } else {
1194 //print uninterpreted source {@tagname ...} for unknown tags
1195 int start= node.getStartPosition();
1196 String text= fSource.substring(start, start + node.getLength());
1197 fBuf.append(removeDocLineIntros(text));
1198 }
1199
1200 if (isLink || isCode)
1201 fBuf.append("</code>"); //$NON-NLS-1$
1202 if (isLiteral || isCode)
1203 fLiteralContent--;
1204
1205 }
1206
1207 private boolean handleValueTag(TagElement node) {
1208
1209 List<? extends ASTNode> fragments= node.fragments();
1210 try {
1211 if (fragments.isEmpty()) {
1212 if (fMember instanceof IField && JdtFlags.isStatic(fMember) && JdtFlags.isFinal(fMember)) {
1213 IField field= (IField) fMember;
1214 return handleConstantValue(field, false);
1215 }
1216 } else if (fragments.size() == 1) {
1217 Object first= fragments.get(0);
1218 if (first instanceof MemberRef) {
1219 MemberRef memberRef= (MemberRef) first;
1220 if (memberRef.getQualifier() == null) {
1221 SimpleName name= memberRef.getName();
1222 IType type= fMember instanceof IType ? (IType)fMember : fMember.getDeclaringType();
1223 while (type != null) {
1224 IField field= type.getField(name.getIdentifier());
1225 if (field != null && field.exists()) {
1226 if (JdtFlags.isStatic(field) && JdtFlags.isFinal(field))
1227 return handleConstantValue(field, true);
1228 break;
1229 }
1230 type= type.getDeclaringType();
1231 }
1232 }
1233 }
1234 }
1235 } catch (JavaModelException e) {
1236 JavaPlugin.log(e);
1237 }
1238
1239 return false;
1240 }
1241
1242 private boolean handleConstantValue(IField field, boolean link) throws JavaModelException {
1243 String text= null;
1244
1245 ISourceRange nameRange= field.getNameRange();
1246 if (SourceRange.isAvailable(nameRange)) {
1247 CompilationUnit cuNode= SharedASTProvider.getAST(field.getTypeRoot(), SharedASTProvider.WAIT_ACTIVE_ONLY, null);
1248 if (cuNode != null) {
1249 ASTNode nameNode= NodeFinder.perform(cuNode, nameRange);
1250 if (nameNode instanceof SimpleName) {
1251 IBinding binding= ((SimpleName) nameNode).resolveBinding();
1252 if (binding instanceof IVariableBinding) {
1253 IVariableBinding variableBinding= (IVariableBinding) binding;
1254 Object constantValue= variableBinding.getConstantValue();
1255 if (constantValue != null) {
1256 if (constantValue instanceof String) {
1257 text= ASTNodes.getEscapedStringLiteral((String) constantValue);
1258 } else {
1259 text= constantValue.toString(); // Javadoc tool is even worse for chars...
1260 }
1261 }
1262 }
1263 }
1264 }
1265 }
1266
1267 if (text == null) {
1268 Object constant= field.getConstant();
1269 if (constant != null) {
1270 text= constant.toString();
1271 }
1272 }
1273
1274 if (text != null) {
1275 text= HTMLPrinter.convertToHTMLContentWithWhitespace(text);
1276 if (link) {
1277 String uri;
1278 try {
1279 uri= JavaElementLinks.createURI(JavaElementLinks.JAVADOC_SCHEME, field);
1280 fBuf.append(JavaElementLinks.createLink(uri, text));
1281 } catch (URISyntaxException e) {
1282 JavaPlugin.log(e);
1283 return false;
1284 }
1285 } else {
1286 handleText(text);
1287 }
1288 return true;
1289 }
1290 return false;
1291 }
1292
1293 private boolean handleDocRoot(TagElement node) {
1294 if (!TagElement.TAG_DOCROOT.equals(node.getTagName()))
1295 return false;
1296
1297 try {
1298 String url= null;
1299 if (fMember.isBinary()) {
1300 URL javadocBaseLocation= JavaUI.getJavadocBaseLocation(fMember);
1301 if (javadocBaseLocation != null) {
1302 url= javadocBaseLocation.toExternalForm();
1303 }
1304 } else {
1305 IPackageFragmentRoot srcRoot= JavaModelUtil.getPackageFragmentRoot(fMember);
1306 if (srcRoot != null) {
1307 IResource resource= srcRoot.getResource();
1308 if (resource != null) {
1309 /*
1310 * Too bad: Browser widget knows nothing about EFS and custom URL handlers,
1311 * so IResource#getLocationURI() does not work in all cases.
1312 * We only support the local file system for now.
1313 * A solution could be https://bugs.eclipse.org/bugs/show_bug.cgi?id=149022 .
1314 */
1315 IPath location= resource.getLocation();
1316 if (location != null) {
1317 url= location.toFile().toURI().toASCIIString();
1318 }
1319 }
1320
1321 }
1322 }
1323 if (url != null) {
1324 if (url.endsWith("/")) { //$NON-NLS-1$
1325 url= url.substring(0, url.length() -1);
1326 }
1327 fBuf.append(url);
1328 return true;
1329 }
1330 } catch (JavaModelException e) {
1331 }
1332 return false;
1333 }
1334
1335
1336 /**
1337 * Handle {&#64;inheritDoc}.
1338 *
1339 * @param node the node
1340 * @return <code>true</code> iff the node was an {&#64;inheritDoc} node and has been handled
1341 */
1342 private boolean handleInheritDoc(TagElement node) {
1343 if (! TagElement.TAG_INHERITDOC.equals(node.getTagName()))
1344 return false;
1345 return fJavadocLookup.generated_8554163332790850784(this, node);
1346 }
1347
1348 private boolean handleInherited(CharSequence inherited) {
1349 if (inherited == null)
1350 return false;
1351
1352 fBuf.append(inherited);
1353 return true;
1354 }
1355
1356 private void handleBlockTags(String title, List<TagElement> tags) {
1357 if (tags.isEmpty())
1358 return;
1359
1360 handleBlockTagTitle(title);
1361
1362 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1363 TagElement tag= iter.next();
1364 fBuf.append(BlOCK_TAG_ENTRY_START);
1365 if (TagElement.TAG_SEE.equals(tag.getTagName())) {
1366 handleSeeTag(tag);
1367 } else {
1368 handleContentElements(tag.fragments());
1369 }
1370 fBuf.append(BlOCK_TAG_ENTRY_END);
1371 }
1372 }
1373
1374 private void handleReturnTag(TagElement tag, CharSequence returnDescription) {
1375 if (tag == null && returnDescription == null)
1376 return;
1377
1378 handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_returns_section);
1379 fBuf.append(BlOCK_TAG_ENTRY_START);
1380 if (tag != null)
1381 handleContentElements(tag.fragments());
1382 else
1383 fBuf.append(returnDescription);
1384 fBuf.append(BlOCK_TAG_ENTRY_END);
1385 }
1386
1387 private void handleBlockTags(List<TagElement> tags) {
1388 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1389 TagElement tag= iter.next();
1390 handleBlockTagTitle(tag.getTagName());
1391 fBuf.append(BlOCK_TAG_ENTRY_START);
1392 handleContentElements(tag.fragments());
1393 fBuf.append(BlOCK_TAG_ENTRY_END);
1394 }
1395 }
1396
1397 private void handleBlockTagTitle(String title) {
1398 fBuf.append("<dt>"); //$NON-NLS-1$
1399 fBuf.append(title);
1400 fBuf.append("</dt>"); //$NON-NLS-1$
1401 }
1402
1403 private void handleSeeTag(TagElement tag) {
1404 handleLink(tag.fragments());
1405 }
1406
1407 private void handleExceptionTags(List<TagElement> tags, List<String> exceptionNames, CharSequence[] exceptionDescriptions) {
1408 if (tags.size() == 0 && containsOnlyNull(exceptionNames))
1409 return;
1410
1411 handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_throws_section);
1412
1413 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1414 TagElement tag= iter.next();
1415 fBuf.append(BlOCK_TAG_ENTRY_START);
1416 handleThrowsTag(tag);
1417 fBuf.append(BlOCK_TAG_ENTRY_END);
1418 }
1419 for (int i= 0; i < exceptionDescriptions.length; i++) {
1420 CharSequence description= exceptionDescriptions[i];
1421 String name= exceptionNames.get(i);
1422 if (name != null) {
1423 fBuf.append(BlOCK_TAG_ENTRY_START);
1424 handleLink(Collections.singletonList(fJavadoc.getAST().newSimpleName(name)));
1425 if (description != null) {
1426 fBuf.append(JavaElementLabels.CONCAT_STRING);
1427 fBuf.append(description);
1428 }
1429 fBuf.append(BlOCK_TAG_ENTRY_END);
1430 }
1431 }
1432 }
1433
1434 private void handleThrowsTag(TagElement tag) {
1435 List<? extends ASTNode> fragments= tag.fragments();
1436 int size= fragments.size();
1437 if (size > 0) {
1438 handleLink(fragments.subList(0, 1));
1439 if (size > 1) {
1440 fBuf.append(JavaElementLabels.CONCAT_STRING);
1441 handleContentElements(fragments.subList(1, size));
1442 }
1443 }
1444 }
1445
1446 private void handleParameterTags(List<TagElement> tags, List<String> parameterNames, CharSequence[] parameterDescriptions) {
1447 if (tags.size() == 0 && containsOnlyNull(parameterNames))
1448 return;
1449
1450 handleBlockTagTitle(JavaDocMessages.JavaDoc2HTMLTextReader_parameters_section);
1451
1452 for (Iterator<TagElement> iter= tags.iterator(); iter.hasNext(); ) {
1453 TagElement tag= iter.next();
1454 fBuf.append(BlOCK_TAG_ENTRY_START);
1455 handleParamTag(tag);
1456 fBuf.append(BlOCK_TAG_ENTRY_END);
1457 }
1458 for (int i= 0; i < parameterDescriptions.length; i++) {
1459 CharSequence description= parameterDescriptions[i];
1460 String name= parameterNames.get(i);
1461 if (name != null) {
1462 fBuf.append(BlOCK_TAG_ENTRY_START);
1463 fBuf.append(PARAM_NAME_START);
1464 fBuf.append(name);
1465 fBuf.append(PARAM_NAME_END);
1466 if (description != null)
1467 fBuf.append(description);
1468 fBuf.append(BlOCK_TAG_ENTRY_END);
1469 }
1470 }
1471 }
1472
1473 private void handleParamTag(TagElement tag) {
1474 List<? extends ASTNode> fragments= tag.fragments();
1475 int i= 0;
1476 int size= fragments.size();
1477 if (size > 0) {
1478 Object first= fragments.get(0);
1479 fBuf.append(PARAM_NAME_START);
1480 if (first instanceof SimpleName) {
1481 String name= ((SimpleName) first).getIdentifier();
1482 fBuf.append(name);
1483 i++;
1484 } else if (first instanceof TextElement) {
1485 String firstText= ((TextElement) first).getText();
1486 if ("<".equals(firstText)) { //$NON-NLS-1$
1487 fBuf.append("&lt;"); //$NON-NLS-1$
1488 i++;
1489 if (size > 1) {
1490 Object second= fragments.get(1);
1491 if (second instanceof SimpleName) {
1492 String name= ((SimpleName) second).getIdentifier();
1493 fBuf.append(name);
1494 i++;
1495 if (size > 2) {
1496 Object third= fragments.get(2);
1497 String thirdText= ((TextElement) third).getText();
1498 if (">".equals(thirdText)) { //$NON-NLS-1$
1499 fBuf.append("&gt;"); //$NON-NLS-1$
1500 i++;
1501 }
1502 }
1503 }
1504 }
1505 }
1506 }
1507 fBuf.append(PARAM_NAME_END);
1508
1509 handleContentElements(fragments.subList(i, fragments.size()));
1510 }
1511 }
1512
1513 private void handleLink(List<? extends ASTNode> fragments) {
1514 //TODO: Javadoc shortens type names to minimal length according to context
1515 int fs= fragments.size();
1516 if (fs > 0) {
1517 Object first= fragments.get(0);
1518 String refTypeName= null;
1519 String refMemberName= null;
1520 String[] refMethodParamTypes= null;
1521 String[] refMethodParamNames= null;
1522 if (first instanceof Name) {
1523 Name name = (Name) first;
1524 refTypeName= name.getFullyQualifiedName();
1525 } else if (first instanceof MemberRef) {
1526 MemberRef memberRef= (MemberRef) first;
1527 Name qualifier= memberRef.getQualifier();
1528 refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
1529 refMemberName= memberRef.getName().getIdentifier();
1530 } else if (first instanceof MethodRef) {
1531 MethodRef methodRef= (MethodRef) first;
1532 Name qualifier= methodRef.getQualifier();
1533 refTypeName= qualifier == null ? "" : qualifier.getFullyQualifiedName(); //$NON-NLS-1$
1534 refMemberName= methodRef.getName().getIdentifier();
1535 List<MethodRefParameter> params= methodRef.parameters();
1536 int ps= params.size();
1537 refMethodParamTypes= new String[ps];
1538 refMethodParamNames= new String[ps];
1539 for (int i= 0; i < ps; i++) {
1540 MethodRefParameter param= params.get(i);
1541 refMethodParamTypes[i]= ASTNodes.asString(param.getType());
1542 SimpleName paramName= param.getName();
1543 if (paramName != null)
1544 refMethodParamNames[i]= paramName.getIdentifier();
1545 }
1546 }
1547
1548 if (refTypeName != null) {
1549 fBuf.append("<a href='"); //$NON-NLS-1$
1550 try {
1551 String scheme= JavaElementLinks.JAVADOC_SCHEME;
1552 String uri= JavaElementLinks.createURI(scheme, fMember, refTypeName, refMemberName, refMethodParamTypes);
1553 fBuf.append(uri);
1554 } catch (URISyntaxException e) {
1555 JavaPlugin.log(e);
1556 }
1557 fBuf.append("'>"); //$NON-NLS-1$
1558 if (fs > 1 && !(fs == 2 && isWhitespaceTextElement(fragments.get(1)))) {
1559 handleContentElements(fragments.subList(1, fs), true);
1560 } else {
1561 fBuf.append(refTypeName);
1562 if (refMemberName != null) {
1563 if (refTypeName.length() > 0) {
1564 fBuf.append('.');
1565 }
1566 fBuf.append(refMemberName);
1567 if (refMethodParamTypes != null) {
1568 fBuf.append('(');
1569 for (int i= 0; i < refMethodParamTypes.length; i++) {
1570 String pType= refMethodParamTypes[i];
1571 fBuf.append(pType);
1572 String pName= refMethodParamNames[i];
1573 if (pName != null) {
1574 fBuf.append(' ').append(pName);
1575 }
1576 if (i < refMethodParamTypes.length - 1) {
1577 fBuf.append(", "); //$NON-NLS-1$
1578 }
1579 }
1580 fBuf.append(')');
1581 }
1582 }
1583 }
1584 fBuf.append("</a>"); //$NON-NLS-1$
1585 } else {
1586 handleContentElements(fragments);
1587 }
1588 }
1589 }
1590
1591 private static boolean isWhitespaceTextElement(Object fragment) {
1592 if (!(fragment instanceof TextElement))
1593 return false;
1594
1595 TextElement textElement= (TextElement) fragment;
1596 return textElement.getText().trim().length() == 0;
1597 }
1598
1599 private boolean containsOnlyNull(List<String> parameterNames) {
1600 for (Iterator<String> iter= parameterNames.iterator(); iter.hasNext(); ) {
1601 if (iter.next() != null)
1602 return false;
1603 }
1604 return true;
1605 }
1606
1607 public JavadocContentAccess2 generated_4672312213436743719(IMethod method, JavadocLookup javadoclookup) {
1608 javadoclookup.fContentAccesses.put(method, this);
1609 return this;
1610 }
1611
1612}