### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: model/org/eclipse/jdt/internal/core/JavadocConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java,v retrieving revision 1.7 diff -u -r1.7 JavadocConstants.java --- model/org/eclipse/jdt/internal/core/JavadocConstants.java 10 May 2006 18:03:47 -0000 1.7 +++ model/org/eclipse/jdt/internal/core/JavadocConstants.java 26 Jun 2008 10:33:58 -0000 @@ -14,6 +14,7 @@ String ANCHOR_PREFIX_END = "\""; //$NON-NLS-1$ String ANCHOR_PREFIX_START = ""); //$NON-NLS-1$ } public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException { - final String contents = getJavadocContents(monitor); - if (contents == null) return null; - final int indexOfStartOfClassData = contents.indexOf(JavadocConstants.START_OF_CLASS_DATA); - if (indexOfStartOfClassData == -1) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this)); - int indexOfNextSummary = contents.indexOf(JavadocConstants.NESTED_CLASS_SUMMARY); - if (this.isEnum() && indexOfNextSummary == -1) { - // try to find enum constant summary start - indexOfNextSummary = contents.indexOf(JavadocConstants.ENUM_CONSTANT_SUMMARY); - } - if (this.isAnnotation() && indexOfNextSummary == -1) { - // try to find required enum constant summary start - indexOfNextSummary = contents.indexOf(JavadocConstants.ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY); - if (indexOfNextSummary == -1) { - // try to find optional enum constant summary start - indexOfNextSummary = contents.indexOf(JavadocConstants.ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY); - } - } - if (indexOfNextSummary == -1) { - // try to find field summary start - indexOfNextSummary = contents.indexOf(JavadocConstants.FIELD_SUMMARY); - } - if (indexOfNextSummary == -1) { - // try to find constructor summary start - indexOfNextSummary = contents.indexOf(JavadocConstants.CONSTRUCTOR_SUMMARY); - } - if (indexOfNextSummary == -1) { - // try to find method summary start - indexOfNextSummary = contents.indexOf(JavadocConstants.METHOD_SUMMARY); - } - if (indexOfNextSummary == -1) { - // we take the end of class data - indexOfNextSummary = contents.indexOf(JavadocConstants.END_OF_CLASS_DATA); - } - if (indexOfNextSummary == -1) { - throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this)); - } - /* - * Check out to cut off the hierarchy see 119844 - * We remove what the contents between the start of class data and the first

- */ - int start = indexOfStartOfClassData + JavadocConstants.START_OF_CLASS_DATA_LENGTH; - int indexOfFirstParagraph = contents.indexOf("

", start); //$NON-NLS-1$ - if (indexOfFirstParagraph == -1) { - indexOfFirstParagraph = contents.indexOf("

", start); //$NON-NLS-1$ - } - if (indexOfFirstParagraph != -1 && indexOfFirstParagraph < indexOfNextSummary) { - start = indexOfFirstParagraph; - } - return contents.substring(start, indexOfNextSummary); + JavadocContents javadocContents = getJavadocContents(monitor); + if (javadocContents == null) return null; + return javadocContents.getTypeDoc(); } -public String getJavadocContents(IProgressMonitor monitor) throws JavaModelException { +public JavadocContents getJavadocContents(IProgressMonitor monitor) throws JavaModelException { PerProjectInfo projectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(this.getJavaProject().getProject()); - String cachedJavadoc = null; + JavadocContents cachedJavadoc = null; synchronized (projectInfo.javadocCache) { - cachedJavadoc = (String) projectInfo.javadocCache.get(this); + cachedJavadoc = (JavadocContents) projectInfo.javadocCache.get(this); } if (cachedJavadoc != null && cachedJavadoc != EMPTY_JAVADOC) { return cachedJavadoc; @@ -1057,9 +1011,10 @@ if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); final String contents = getURLContents(String.valueOf(pathBuffer)); + JavadocContents javadocContents = new JavadocContents(this, contents); synchronized (projectInfo.javadocCache) { - projectInfo.javadocCache.put(this, contents); + projectInfo.javadocCache.put(this, javadocContents); } - return contents; + return javadocContents; } } Index: model/org/eclipse/jdt/internal/core/JavadocContents.java =================================================================== RCS file: model/org/eclipse/jdt/internal/core/JavadocContents.java diff -N model/org/eclipse/jdt/internal/core/JavadocContents.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ model/org/eclipse/jdt/internal/core/JavadocContents.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,335 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core; + +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaModelStatusConstants; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray; +import org.eclipse.jdt.internal.core.util.Util; + +public class JavadocContents { + private static final int[] UNKNOWN_FORMAT = new int[0]; + + private BinaryType type; + private String content; + + private int childrenStart; + + private int[] typeDocRange; + private HashtableOfObjectToIntArray childrenDocRanges; + + public JavadocContents(BinaryType type, String content) { + this.type = type; + this.content = content; + } + + /* + * Return the full content of the javadoc + */ + public String getContent() { + return this.content; + } + + /* + * Returns the part of the javadoc that describe the type + */ + public String getTypeDoc() throws JavaModelException { + if (this.content == null) return null; + + synchronized (this) { + if (this.typeDocRange == null) { + this.computeTypeRange(); + } + } + + if (typeDocRange != null) { + if (typeDocRange == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, type)); + return this.content.substring(typeDocRange[0], typeDocRange[1]); + } + return null; + } + + /* + * Returns the part of the javadoc that describe a child of the type (field, method) + */ + public String getChildDoc(IJavaElement child) throws JavaModelException { + if (this.content == null) return null; + + synchronized (this) { + if (this.childrenDocRanges == null) { + this.computeChildrenRanges(); + } + } + + int[] range = this.childrenDocRanges.get(child); + if (range != null) { + if (range == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, child)); + return this.content.substring(range[0], range[1]); + } + return null; + } + + /* + * Compute the ranges of the parts of the javadoc that describe each child of the type (fields, methods) + */ + private void computeChildrenRanges() throws JavaModelException { + this.childrenDocRanges = new HashtableOfObjectToIntArray(); + + IJavaElement[] children = this.type.getChildren(); + + int length = children.length; + if (length > 0) { + String typeQualifiedName = null; + if (type.isMember()) { + IType currentType = type; + StringBuffer buffer = new StringBuffer(); + while (currentType != null) { + buffer.insert(0, currentType.getElementName()); + currentType = currentType.getDeclaringType(); + if (currentType != null) { + buffer.insert(0, '.'); + } + } + typeQualifiedName = new String(buffer.toString()); + } else { + typeQualifiedName = type.getElementName(); + } + + // try to find the next separator part + int lastIndex = content.indexOf(JavadocConstants.SEPARATOR_START, this.childrenStart); + + // try to find constructor detail start + int indexOfConstructorDetails = content.indexOf(JavadocConstants.CONSTRUCTOR_DETAIL, lastIndex); + lastIndex = indexOfConstructorDetails == -1 ? lastIndex : indexOfConstructorDetails; + + // try to find method detail start + int indexOfMethodDetails = content.indexOf(JavadocConstants.METHOD_DETAIL, lastIndex); + lastIndex = indexOfMethodDetails == -1 ? lastIndex : indexOfMethodDetails; + + // we take the end of class data + int indexOfEndOfClassData = content.indexOf(JavadocConstants.END_OF_CLASS_DATA, lastIndex); + + // try to find the field detail end + int indexOfFieldsBottom = + indexOfConstructorDetails != -1 ? indexOfConstructorDetails : + indexOfMethodDetails != -1 ? indexOfMethodDetails: + indexOfEndOfClassData; + + // try to find the constructor detail end + int indexOfConstructorsBottom = + indexOfMethodDetails != -1 ? indexOfMethodDetails: + indexOfEndOfClassData; + + // try to find the method detail end + int indexOfMethodsBottom = indexOfEndOfClassData; + + String[] anchors = new String[length]; + int anchorCount = 0; + + // compute the end of the anchor prefix of each children + for (int i = 0; i < length; i++) { + IJavaElement child = children[i]; + switch (child.getElementType()) { + case IJavaElement.METHOD: + anchors[anchorCount] = computeMethodAnchorPrefixEnd((BinaryMethod)child, typeQualifiedName); + children[anchorCount] = child; + anchorCount++; + break; + case IJavaElement.FIELD: + anchors[anchorCount] = child.getElementName() + JavadocConstants.ANCHOR_PREFIX_END; + children[anchorCount] = child; + anchorCount++; + break; + } + } + + int fromIndex = 0; + int index; + + // check each anchor in the javadoc + while ((index = content.indexOf(JavadocConstants.ANCHOR_PREFIX_START, fromIndex)) != -1) { + fromIndex = index + 1; + + int anchorEndStart = index + JavadocConstants.ANCHOR_PREFIX_START_LENGHT; + + // check if this anchor is an anchor of a known child + done : for (int i = 0; i < anchorCount; i++) { + String anchor = anchors[i]; + IJavaElement child = children[i]; + + if (anchor != null && content.startsWith(anchor, anchorEndStart)) { + + // the child corresponding to the anchor is found. + // the anchor is removed from the list of not found anchors + anchors[i] = null; + + // try to find the bottom of the section + int indexOfBottom = -1; + switch (child.getElementType()) { + case IJavaElement.METHOD: + indexOfBottom = ((BinaryMethod)child).isConstructor() ? indexOfConstructorsBottom : indexOfMethodsBottom; + break; + case IJavaElement.FIELD: + indexOfBottom = indexOfFieldsBottom; + break; + } + + if (indexOfBottom != -1) { + // try to find the end of the anchor + int indexOfEndLink = content.indexOf(JavadocConstants.ANCHOR_SUFFIX, anchorEndStart + anchor.length()); + if (indexOfEndLink != -1) { + // try to find the next anchor + int indexOfNextElement = content.indexOf(JavadocConstants.ANCHOR_PREFIX_START, indexOfEndLink); + + int javadocStart = indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH; + int javadocEnd = indexOfNextElement == -1 ? indexOfBottom : Math.min(indexOfNextElement, indexOfBottom); + this.childrenDocRanges.put(child, new int[]{javadocStart, javadocEnd}); + } else { + // the anchor has no suffix + this.childrenDocRanges.put(child, UNKNOWN_FORMAT); + } + } else { + // the detail section has no bottom + this.childrenDocRanges.put(child, UNKNOWN_FORMAT); + } + break done; + } + } + } + } + } + + private String computeMethodAnchorPrefixEnd(BinaryMethod method, String typeQualifiedName) throws JavaModelException { + String methodName = method.getElementName(); + if (method.isConstructor()) { + methodName = typeQualifiedName; + } + IBinaryMethod info = (IBinaryMethod) method.getElementInfo(); + + char[] genericSignature = info.getGenericSignature(); + String anchor = null; + if (genericSignature != null) { + genericSignature = CharOperation.replaceOnCopy(genericSignature, '/', '.'); + anchor = Util.toAnchor(genericSignature, methodName, Flags.isVarargs(method.getFlags())); + if (anchor == null) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, method)); + } else { + anchor = Signature.toString(method.getSignature().replace('/', '.'), methodName, null, true, false, Flags.isVarargs(method.getFlags())); + } + IType declaringType = this.type; + if (declaringType.isMember()) { + int depth = 0; + final String packageFragmentName = declaringType.getPackageFragment().getElementName(); + // might need to remove a part of the signature corresponding to the synthetic argument + final IJavaProject javaProject = declaringType.getJavaProject(); + char[][] typeNames = CharOperation.splitOn('.', typeQualifiedName.toCharArray()); + if (!Flags.isStatic(declaringType.getFlags())) depth++; + StringBuffer typeName = new StringBuffer(); + for (int i = 0, max = typeNames.length; i < max; i++) { + if (typeName.length() == 0) { + typeName.append(typeNames[i]); + } else { + typeName.append('.').append(typeNames[i]); + } + IType resolvedType = javaProject.findType(packageFragmentName, String.valueOf(typeName)); + if (resolvedType != null && resolvedType.isMember() && !Flags.isStatic(resolvedType.getFlags())) depth++; + } + if (depth != 0) { + int indexOfOpeningParen = anchor.indexOf('('); + if (indexOfOpeningParen == -1) return null; + int index = indexOfOpeningParen; + indexOfOpeningParen++; + for (int i = 0; i < depth; i++) { + int indexOfComma = anchor.indexOf(',', index); + if (indexOfComma != -1) { + index = indexOfComma + 2; + } + } + anchor = anchor.substring(0, indexOfOpeningParen) + anchor.substring(index); + } + } + return anchor + JavadocConstants.ANCHOR_PREFIX_END; + } + + /* + * Compute the range of the part of the javadoc that describe the type + */ + private void computeTypeRange() throws JavaModelException { + final int indexOfStartOfClassData = content.indexOf(JavadocConstants.START_OF_CLASS_DATA); + if (indexOfStartOfClassData == -1) { + this.typeDocRange = UNKNOWN_FORMAT; + return; + } + int indexOfNextSeparator = content.indexOf(JavadocConstants.SEPARATOR_START, indexOfStartOfClassData); + if (indexOfNextSeparator == -1) { + this.typeDocRange = UNKNOWN_FORMAT; + return; + } + int indexOfNextSummary = content.indexOf(JavadocConstants.NESTED_CLASS_SUMMARY, indexOfNextSeparator); + if (indexOfNextSummary == -1 && type.isEnum()) { + // try to find enum constant summary start + indexOfNextSummary = content.indexOf(JavadocConstants.ENUM_CONSTANT_SUMMARY, indexOfNextSeparator); + } + if (indexOfNextSummary == -1 && type.isAnnotation()) { + // try to find required enum constant summary start + indexOfNextSummary = content.indexOf(JavadocConstants.ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY, indexOfNextSeparator); + if (indexOfNextSummary == -1) { + // try to find optional enum constant summary start + indexOfNextSummary = content.indexOf(JavadocConstants.ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY, indexOfNextSeparator); + } + } + if (indexOfNextSummary == -1) { + // try to find field summary start + indexOfNextSummary = content.indexOf(JavadocConstants.FIELD_SUMMARY, indexOfNextSeparator); + } + if (indexOfNextSummary == -1) { + // try to find constructor summary start + indexOfNextSummary = content.indexOf(JavadocConstants.CONSTRUCTOR_SUMMARY, indexOfNextSeparator); + } + if (indexOfNextSummary == -1) { + // try to find method summary start + indexOfNextSummary = content.indexOf(JavadocConstants.METHOD_SUMMARY, indexOfNextSeparator); + } + + if (indexOfNextSummary == -1) { + // we take the end of class data + indexOfNextSummary = content.indexOf(JavadocConstants.END_OF_CLASS_DATA, indexOfNextSeparator); + } else { + // improve performance of computation of children ranges + this.childrenStart = indexOfNextSummary + 1; + } + + if (indexOfNextSummary == -1) { + this.typeDocRange = UNKNOWN_FORMAT; + return; + } + /* + * Check out to cut off the hierarchy see 119844 + * We remove what the contents between the start of class data and the first

+ */ + int start = indexOfStartOfClassData + JavadocConstants.START_OF_CLASS_DATA_LENGTH; + int indexOfFirstParagraph = content.indexOf("

", start); //$NON-NLS-1$ + if (indexOfFirstParagraph == -1) { + indexOfFirstParagraph = content.indexOf("

", start); //$NON-NLS-1$ + } + if (indexOfFirstParagraph != -1 && indexOfFirstParagraph < indexOfNextSummary) { + start = indexOfFirstParagraph; + } + + this.typeDocRange = new int[]{start, indexOfNextSummary}; + } +}