]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/text/javadoc/JavadocContentAccess2.java
b158794b34ece6261b6170deb100e8335ef0aa3f
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / internal / ui / text / javadoc / JavadocContentAccess2.java
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  *******************************************************************************/
12 package org.eclipse.jdt.internal.ui.text.javadoc;
13
14 import java.io.IOException;
15 import java.io.Reader;
16 import java.net.URISyntaxException;
17 import java.net.URL;
18 import java.text.MessageFormat;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.eclipse.core.runtime.IPath;
28
29 import org.eclipse.core.resources.IResource;
30
31 import org.eclipse.jface.internal.text.html.HTMLPrinter;
32
33 import org.eclipse.jdt.core.IBuffer;
34 import org.eclipse.jdt.core.IField;
35 import org.eclipse.jdt.core.IJavaProject;
36 import org.eclipse.jdt.core.IMember;
37 import org.eclipse.jdt.core.IMethod;
38 import org.eclipse.jdt.core.IPackageFragmentRoot;
39 import org.eclipse.jdt.core.ISourceRange;
40 import org.eclipse.jdt.core.IType;
41 import org.eclipse.jdt.core.ITypeHierarchy;
42 import org.eclipse.jdt.core.JavaCore;
43 import org.eclipse.jdt.core.JavaModelException;
44 import org.eclipse.jdt.core.Signature;
45 import org.eclipse.jdt.core.SourceRange;
46 import org.eclipse.jdt.core.dom.ASTNode;
47 import org.eclipse.jdt.core.dom.ASTParser;
48 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
49 import org.eclipse.jdt.core.dom.CompilationUnit;
50 import org.eclipse.jdt.core.dom.IBinding;
51 import org.eclipse.jdt.core.dom.IVariableBinding;
52 import org.eclipse.jdt.core.dom.Javadoc;
53 import org.eclipse.jdt.core.dom.MemberRef;
54 import org.eclipse.jdt.core.dom.MethodRef;
55 import org.eclipse.jdt.core.dom.MethodRefParameter;
56 import org.eclipse.jdt.core.dom.Name;
57 import org.eclipse.jdt.core.dom.NodeFinder;
58 import org.eclipse.jdt.core.dom.SimpleName;
59 import org.eclipse.jdt.core.dom.TagElement;
60 import org.eclipse.jdt.core.dom.TextElement;
61
62 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
63 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
64 import org.eclipse.jdt.internal.corext.util.JdtFlags;
65 import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
66 import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache;
67
68 import org.eclipse.jdt.ui.JavaElementLabels;
69 import org.eclipse.jdt.ui.JavaUI;
70 import org.eclipse.jdt.ui.JavadocContentAccess;
71 import org.eclipse.jdt.ui.SharedASTProvider;
72
73 import org.eclipse.jdt.internal.ui.JavaPlugin;
74 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
75 import 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  */
88 public 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 }