View | Details | Raw Unified | Return to bug 237241 | Differences between
and this patch

Collapse All | Expand All

(-)model/org/eclipse/jdt/internal/core/JavadocConstants.java (+3 lines)
Lines 14-23 Link Here
14
14
15
	String ANCHOR_PREFIX_END = "\""; //$NON-NLS-1$
15
	String ANCHOR_PREFIX_END = "\""; //$NON-NLS-1$
16
	String ANCHOR_PREFIX_START = "<A NAME=\""; //$NON-NLS-1$
16
	String ANCHOR_PREFIX_START = "<A NAME=\""; //$NON-NLS-1$
17
	int ANCHOR_PREFIX_START_LENGHT = ANCHOR_PREFIX_START.length();
17
	String ANCHOR_SUFFIX = "</A>"; //$NON-NLS-1$
18
	String ANCHOR_SUFFIX = "</A>"; //$NON-NLS-1$
18
	int ANCHOR_SUFFIX_LENGTH = JavadocConstants.ANCHOR_SUFFIX.length();
19
	int ANCHOR_SUFFIX_LENGTH = JavadocConstants.ANCHOR_SUFFIX.length();
19
	String CONSTRUCTOR_DETAIL = "<!-- ========= CONSTRUCTOR DETAIL ======== -->"; //$NON-NLS-1$
20
	String CONSTRUCTOR_DETAIL = "<!-- ========= CONSTRUCTOR DETAIL ======== -->"; //$NON-NLS-1$
20
	String CONSTRUCTOR_SUMMARY = "<!-- ======== CONSTRUCTOR SUMMARY ======== -->"; //$NON-NLS-1$
21
	String CONSTRUCTOR_SUMMARY = "<!-- ======== CONSTRUCTOR SUMMARY ======== -->"; //$NON-NLS-1$
22
	String FIELD_DETAIL= "<!-- ============ FIELD DETAIL =========== -->"; //$NON-NLS-1$
21
	String FIELD_SUMMARY = "<!-- =========== FIELD SUMMARY =========== -->"; //$NON-NLS-1$
23
	String FIELD_SUMMARY = "<!-- =========== FIELD SUMMARY =========== -->"; //$NON-NLS-1$
22
	String ENUM_CONSTANT_SUMMARY = "<!-- =========== ENUM CONSTANT SUMMARY =========== -->"; //$NON-NLS-1$
24
	String ENUM_CONSTANT_SUMMARY = "<!-- =========== ENUM CONSTANT SUMMARY =========== -->"; //$NON-NLS-1$
23
	String ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY = "<!-- =========== ANNOTATION TYPE REQUIRED MEMBER SUMMARY =========== -->"; //$NON-NLS-1$
25
	String ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY = "<!-- =========== ANNOTATION TYPE REQUIRED MEMBER SUMMARY =========== -->"; //$NON-NLS-1$
Lines 29-34 Link Here
29
	String METHOD_SUMMARY = "<!-- ========== METHOD SUMMARY =========== -->"; //$NON-NLS-1$
31
	String METHOD_SUMMARY = "<!-- ========== METHOD SUMMARY =========== -->"; //$NON-NLS-1$
30
	String NESTED_CLASS_SUMMARY = "<!-- ======== NESTED CLASS SUMMARY ======== -->"; //$NON-NLS-1$
32
	String NESTED_CLASS_SUMMARY = "<!-- ======== NESTED CLASS SUMMARY ======== -->"; //$NON-NLS-1$
31
	String PACKAGE_FILE_NAME = "package-summary.html"; //$NON-NLS-1$
33
	String PACKAGE_FILE_NAME = "package-summary.html"; //$NON-NLS-1$
34
	String SEPARATOR_START = "<!-- ="; //$NON-NLS-1$
32
	String START_OF_CLASS_DATA = "<!-- ======== START OF CLASS DATA ======== -->"; //$NON-NLS-1$
35
	String START_OF_CLASS_DATA = "<!-- ======== START OF CLASS DATA ======== -->"; //$NON-NLS-1$
33
	int START_OF_CLASS_DATA_LENGTH = JavadocConstants.START_OF_CLASS_DATA.length();
36
	int START_OF_CLASS_DATA_LENGTH = JavadocConstants.START_OF_CLASS_DATA.length();
34
}
37
}
(-)model/org/eclipse/jdt/internal/core/BinaryField.java (-22 / +3 lines)
Lines 14-20 Link Here
14
import org.eclipse.jdt.core.Flags;
14
import org.eclipse.jdt.core.Flags;
15
import org.eclipse.jdt.core.IAnnotation;
15
import org.eclipse.jdt.core.IAnnotation;
16
import org.eclipse.jdt.core.IField;
16
import org.eclipse.jdt.core.IField;
17
import org.eclipse.jdt.core.IJavaModelStatusConstants;
18
import org.eclipse.jdt.core.JavaModelException;
17
import org.eclipse.jdt.core.JavaModelException;
19
import org.eclipse.jdt.core.Signature;
18
import org.eclipse.jdt.core.Signature;
20
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
19
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
Lines 115-140 Link Here
115
	}
114
	}
116
}
115
}
117
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
116
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
118
	String contents = ((BinaryType) getDeclaringType()).getJavadocContents(monitor);
117
	JavadocContents javadocContents = ((BinaryType) this.getDeclaringType()).getJavadocContents(monitor);
119
	if (contents == null) return null;
118
	if (javadocContents == null) return null;
120
	int indexAnchor = contents.indexOf(
119
	return javadocContents.getFieldDoc(this);
121
			JavadocConstants.ANCHOR_PREFIX_START + getElementName() + JavadocConstants.ANCHOR_PREFIX_END);
122
	if (indexAnchor == -1) {
123
		// this might be the case for a private field that has no javadoc entry
124
		return null;
125
	}
126
	int indexOfEndLink = contents.indexOf(JavadocConstants.ANCHOR_SUFFIX, indexAnchor);
127
	if (indexOfEndLink == -1) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
128
	int indexOfNextField = contents.indexOf(JavadocConstants.ANCHOR_PREFIX_START, indexOfEndLink);
129
	int indexOfBottom = contents.indexOf(JavadocConstants.CONSTRUCTOR_DETAIL, indexOfEndLink);
130
	if (indexOfBottom == -1) {
131
		indexOfBottom = contents.indexOf(JavadocConstants.METHOD_DETAIL, indexOfEndLink);
132
		if (indexOfBottom == -1) {
133
			indexOfBottom = contents.indexOf(JavadocConstants.END_OF_CLASS_DATA, indexOfEndLink);
134
		}
135
	}
136
	indexOfNextField= Math.min(indexOfNextField, indexOfBottom);
137
	if (indexOfNextField == -1) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
138
	return contents.substring(indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH, indexOfNextField);
139
}
120
}
140
}
121
}
(-)model/org/eclipse/jdt/internal/core/BinaryMethod.java (-79 / +13 lines)
Lines 188-202 Link Here
188
		if ((modifiers & ClassFileConstants.AccSynthetic) != 0) {
188
		if ((modifiers & ClassFileConstants.AccSynthetic) != 0) {
189
			return this.parameterNames = getRawParameterNames(paramCount);
189
			return this.parameterNames = getRawParameterNames(paramCount);
190
		}
190
		}
191
		String javadocContents = null;
191
		JavadocContents javadocContents = null;
192
		IType declaringType = getDeclaringType();
192
		IType declaringType = getDeclaringType();
193
		PerProjectInfo projectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(getJavaProject().getProject());
193
		PerProjectInfo projectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(getJavaProject().getProject());
194
		synchronized (projectInfo.javadocCache) {
194
		synchronized (projectInfo.javadocCache) {
195
			javadocContents = (String) projectInfo.javadocCache.get(declaringType);
195
			javadocContents = (JavadocContents) projectInfo.javadocCache.get(declaringType);
196
			if (javadocContents == null) {
196
			if (javadocContents == null) {
197
				projectInfo.javadocCache.put(declaringType, BinaryType.EMPTY_JAVADOC);
197
				projectInfo.javadocCache.put(declaringType, BinaryType.EMPTY_JAVADOC);
198
			}
198
			}
199
		}
199
		}
200
		
201
		String methodDoc = null;
200
		if (javadocContents == null) {
202
		if (javadocContents == null) {
201
			long timeOut = 50; // default value
203
			long timeOut = 50; // default value
202
			try {
204
			try {
Lines 245-267 Link Here
245
					// ignore
247
					// ignore
246
				}
248
				}
247
			}
249
			}
248
			javadocContents = nameCollector.getJavadoc();
250
			methodDoc = nameCollector.getJavadoc();
249
		} else if (javadocContents != BinaryType.EMPTY_JAVADOC){
251
		} else if (javadocContents != BinaryType.EMPTY_JAVADOC){
250
			// need to extract the part relative to the binary method since javadoc contains the javadoc for the declaring type
252
			// need to extract the part relative to the binary method since javadoc contains the javadoc for the declaring type
251
			try {
253
			try {
252
				javadocContents = extractJavadoc(declaringType, javadocContents);
254
				methodDoc = javadocContents.getMethodDoc(this);
253
			} catch(JavaModelException e) {
255
			} catch(JavaModelException e) {
254
				javadocContents = null;
256
				javadocContents = null;
255
			}
257
			}
256
		}
258
		}
257
		if (javadocContents != null && javadocContents != BinaryType.EMPTY_JAVADOC) {
259
		if (methodDoc != null) {
258
			final int indexOfOpenParen = javadocContents.indexOf('(');
260
			final int indexOfOpenParen = methodDoc.indexOf('(');
259
			if (indexOfOpenParen != -1) {
261
			if (indexOfOpenParen != -1) {
260
				final int indexOfClosingParen = javadocContents.indexOf(')', indexOfOpenParen);
262
				final int indexOfClosingParen = methodDoc.indexOf(')', indexOfOpenParen);
261
				if (indexOfClosingParen != -1) {
263
				if (indexOfClosingParen != -1) {
262
					final char[] paramsSource =
264
					final char[] paramsSource =
263
						CharOperation.replace(
265
						CharOperation.replace(
264
							javadocContents.substring(indexOfOpenParen + 1, indexOfClosingParen).toCharArray(),
266
							methodDoc.substring(indexOfOpenParen + 1, indexOfClosingParen).toCharArray(),
265
							"&nbsp;".toCharArray(), //$NON-NLS-1$
267
							"&nbsp;".toCharArray(), //$NON-NLS-1$
266
							new char[] {' '});
268
							new char[] {' '});
267
					final char[][] params = splitParameters(paramsSource, paramCount);
269
					final char[][] params = splitParameters(paramsSource, paramCount);
Lines 553-628 Link Here
553
	}
555
	}
554
}
556
}
555
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
557
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
556
	IType declaringType = getDeclaringType();
558
	JavadocContents javadocContents = ((BinaryType) this.getDeclaringType()).getJavadocContents(monitor);
557
559
	if (javadocContents == null) return null;
558
	String contents = ((BinaryType) declaringType).getJavadocContents(monitor);
560
	return javadocContents.getMethodDoc(this);
559
	return extractJavadoc(declaringType, contents);
560
}
561
private String extractJavadoc(IType declaringType, String contents) throws JavaModelException {
562
	if (contents == null) return null;
563
564
	String typeQualifiedName = null;
565
	final boolean declaringTypeIsMember = declaringType.isMember();
566
	if (declaringTypeIsMember) {
567
		IType currentType = declaringType;
568
		StringBuffer buffer = new StringBuffer();
569
		while (currentType != null) {
570
			buffer.insert(0, currentType.getElementName());
571
			currentType = currentType.getDeclaringType();
572
			if (currentType != null) {
573
				buffer.insert(0, '.');
574
			}
575
		}
576
		typeQualifiedName = new String(buffer.toString());
577
	} else {
578
		typeQualifiedName = declaringType.getElementName();
579
	}
580
	String methodName = getElementName();
581
	if (isConstructor()) {
582
		methodName = typeQualifiedName;
583
	}
584
	IBinaryMethod info = (IBinaryMethod) getElementInfo();
585
	char[] genericSignature = info.getGenericSignature();
586
	String anchor = null;
587
	if (genericSignature != null) {
588
		genericSignature = CharOperation.replaceOnCopy(genericSignature, '/', '.');
589
		anchor = Util.toAnchor(0, genericSignature, methodName, Flags.isVarargs(getFlags()));
590
		if (anchor == null) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
591
	} else {
592
		anchor = Signature.toString(getSignature().replace('/', '.'), methodName, null, true, false, Flags.isVarargs(getFlags()));
593
	}
594
	if (declaringTypeIsMember && !Flags.isStatic(declaringType.getFlags())) {
595
		int indexOfOpeningParen = anchor.indexOf('(');
596
		if (indexOfOpeningParen == -1) return null;
597
		int index = indexOfOpeningParen +1;
598
		indexOfOpeningParen++;
599
		int indexOfComma = anchor.indexOf(',', index);
600
		if (indexOfComma != -1) {
601
			index = indexOfComma + 2;
602
		} else {
603
			index = anchor.indexOf(')', index);
604
		}
605
		anchor = anchor.substring(0, indexOfOpeningParen) + anchor.substring(index);
606
	}
607
	int indexAnchor = contents.indexOf(JavadocConstants.ANCHOR_PREFIX_START + anchor + JavadocConstants.ANCHOR_PREFIX_END);
608
	if (indexAnchor == -1) {
609
		return null; // method without javadoc
610
	}
611
	int indexOfEndLink = contents.indexOf(JavadocConstants.ANCHOR_SUFFIX, indexAnchor);
612
	if (indexOfEndLink == -1) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
613
	int indexOfNextMethod = contents.indexOf(JavadocConstants.ANCHOR_PREFIX_START, indexOfEndLink);
614
	// find bottom
615
	int indexOfBottom = -1;
616
	if (isConstructor()) {
617
		indexOfBottom = contents.indexOf(JavadocConstants.METHOD_DETAIL, indexOfEndLink);
618
		if (indexOfBottom == -1) {
619
			indexOfBottom = contents.indexOf(JavadocConstants.END_OF_CLASS_DATA, indexOfEndLink);
620
		}
621
	} else {
622
		indexOfBottom = contents.indexOf(JavadocConstants.END_OF_CLASS_DATA, indexOfEndLink);
623
	}
624
	if (indexOfBottom == -1) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
625
	indexOfNextMethod = indexOfNextMethod == -1 ? indexOfBottom : Math.min(indexOfNextMethod, indexOfBottom);
626
	return contents.substring(indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH, indexOfNextMethod);
627
}
561
}
628
}
562
}
(-)model/org/eclipse/jdt/internal/core/BinaryType.java (-56 / +11 lines)
Lines 45-51 Link Here
45
	private static final IMethod[] NO_METHODS = new IMethod[0];
45
	private static final IMethod[] NO_METHODS = new IMethod[0];
46
	private static final IType[] NO_TYPES = new IType[0];
46
	private static final IType[] NO_TYPES = new IType[0];
47
	private static final IInitializer[] NO_INITIALIZERS = new IInitializer[0];
47
	private static final IInitializer[] NO_INITIALIZERS = new IInitializer[0];
48
	public static final String EMPTY_JAVADOC = org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING;
48
	public static final JavadocContents EMPTY_JAVADOC = new JavadocContents(null, org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING);
49
49
50
protected BinaryType(JavaElement parent, String name) {
50
protected BinaryType(JavaElement parent, String name) {
51
	super(parent, name);
51
	super(parent, name);
Lines 991-1052 Link Here
991
		buffer.append("<anonymous>"); //$NON-NLS-1$
991
		buffer.append("<anonymous>"); //$NON-NLS-1$
992
}
992
}
993
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
993
public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException {
994
	final String contents = getJavadocContents(monitor);
994
	JavadocContents javadocContents = getJavadocContents(monitor);
995
	if (contents == null) return null;
995
	if (javadocContents == null) return null;
996
	final int indexOfStartOfClassData = contents.indexOf(JavadocConstants.START_OF_CLASS_DATA);
996
	return javadocContents.getTypeDoc();
997
	if (indexOfStartOfClassData == -1) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
998
	int indexOfNextSummary = contents.indexOf(JavadocConstants.NESTED_CLASS_SUMMARY);
999
	if (isEnum() && indexOfNextSummary == -1) {
1000
		// try to find enum constant summary start
1001
		indexOfNextSummary = contents.indexOf(JavadocConstants.ENUM_CONSTANT_SUMMARY);
1002
	}
1003
	if (isAnnotation() && indexOfNextSummary == -1) {
1004
		// try to find required enum constant summary start
1005
		indexOfNextSummary = contents.indexOf(JavadocConstants.ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY);
1006
		if (indexOfNextSummary == -1) {
1007
			// try to find optional enum constant summary start
1008
			indexOfNextSummary = contents.indexOf(JavadocConstants.ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY);
1009
		}
1010
	}
1011
	if (indexOfNextSummary == -1) {
1012
		// try to find field summary start
1013
		indexOfNextSummary = contents.indexOf(JavadocConstants.FIELD_SUMMARY);
1014
	}
1015
	if (indexOfNextSummary == -1) {
1016
		// try to find constructor summary start
1017
		indexOfNextSummary = contents.indexOf(JavadocConstants.CONSTRUCTOR_SUMMARY);
1018
	}
1019
	if (indexOfNextSummary == -1) {
1020
		// try to find method summary start
1021
		indexOfNextSummary = contents.indexOf(JavadocConstants.METHOD_SUMMARY);
1022
	}
1023
	if (indexOfNextSummary == -1) {
1024
		// we take the end of class data
1025
		indexOfNextSummary = contents.indexOf(JavadocConstants.END_OF_CLASS_DATA);
1026
	}
1027
	if (indexOfNextSummary == -1) {
1028
		throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this));
1029
	}
1030
	/*
1031
	 * Check out to cut off the hierarchy see 119844
1032
	 * We remove what the contents between the start of class data and the first <P>
1033
	 */
1034
	int start = indexOfStartOfClassData + JavadocConstants.START_OF_CLASS_DATA_LENGTH;
1035
	int indexOfFirstParagraph = contents.indexOf("<P>", start); //$NON-NLS-1$
1036
	if (indexOfFirstParagraph == -1) {
1037
		indexOfFirstParagraph = contents.indexOf("<p>", start); //$NON-NLS-1$
1038
	}
1039
	if (indexOfFirstParagraph != -1 && indexOfFirstParagraph < indexOfNextSummary) {
1040
		start = indexOfFirstParagraph;
1041
	}
1042
	return contents.substring(start, indexOfNextSummary);
1043
}
997
}
1044
public String getJavadocContents(IProgressMonitor monitor) throws JavaModelException {
998
public JavadocContents getJavadocContents(IProgressMonitor monitor) throws JavaModelException {
1045
	PerProjectInfo projectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(getJavaProject().getProject());
999
	PerProjectInfo projectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(getJavaProject().getProject());
1046
	String cachedJavadoc = null;
1000
	JavadocContents cachedJavadoc = null;
1047
	synchronized (projectInfo.javadocCache) {
1001
	synchronized (projectInfo.javadocCache) {
1048
		cachedJavadoc = (String) projectInfo.javadocCache.get(this);
1002
		cachedJavadoc = (JavadocContents) projectInfo.javadocCache.get(this);
1049
	}
1003
	}
1004
	
1050
	if (cachedJavadoc != null && cachedJavadoc != EMPTY_JAVADOC) {
1005
	if (cachedJavadoc != null && cachedJavadoc != EMPTY_JAVADOC) {
1051
		return cachedJavadoc;
1006
		return cachedJavadoc;
1052
	}
1007
	}
Lines 1077-1088 Link Here
1077
	}
1032
	}
1078
1033
1079
	pathBuffer.append(pack.getElementName().replace('.', '/')).append('/').append(typeQualifiedName).append(JavadocConstants.HTML_EXTENSION);
1034
	pathBuffer.append(pack.getElementName().replace('.', '/')).append('/').append(typeQualifiedName).append(JavadocConstants.HTML_EXTENSION);
1080
1081
	if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
1035
	if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException();
1082
	final String contents = getURLContents(String.valueOf(pathBuffer));
1036
	final String contents = getURLContents(String.valueOf(pathBuffer));
1037
	JavadocContents javadocContents = new JavadocContents(this, contents);
1083
	synchronized (projectInfo.javadocCache) {
1038
	synchronized (projectInfo.javadocCache) {
1084
		projectInfo.javadocCache.put(this, contents);
1039
		projectInfo.javadocCache.put(this, javadocContents);
1085
	}
1040
	}
1086
	return contents;
1041
	return javadocContents;
1087
}
1042
}
1088
}
1043
}
(-)model/org/eclipse/jdt/internal/core/JavadocContents.java (+497 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jdt.internal.core;
12
13
import org.eclipse.jdt.core.Flags;
14
import org.eclipse.jdt.core.IField;
15
import org.eclipse.jdt.core.IJavaElement;
16
import org.eclipse.jdt.core.IJavaModelStatusConstants;
17
import org.eclipse.jdt.core.IJavaProject;
18
import org.eclipse.jdt.core.IMethod;
19
import org.eclipse.jdt.core.IType;
20
import org.eclipse.jdt.core.JavaModelException;
21
import org.eclipse.jdt.core.Signature;
22
import org.eclipse.jdt.core.compiler.CharOperation;
23
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
24
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray;
25
import org.eclipse.jdt.internal.core.util.Util;
26
27
public class JavadocContents {
28
	private static final int[] UNKNOWN_FORMAT = new int[0]; 
29
	
30
	private BinaryType type;
31
	private String content;
32
	
33
	private int childrenStart;
34
	
35
	private boolean hasComputedChildrenSections = false;
36
	private int indexOfFieldDetails;
37
	private int indexOfConstructorDetails;
38
	private int indexOfMethodDetails;
39
	private int indexOfEndOfClassData;
40
	
41
	private int indexOfFieldsBottom;
42
	private int indexOfConstructorsBottom;
43
	private int indexOfMethodsBottom;
44
	private int indexOfAllMethodsTop;
45
	private int indexOfAllMethodsBottom;
46
	
47
	private int[] typeDocRange;
48
	private HashtableOfObjectToIntArray fieldDocRanges;
49
	private HashtableOfObjectToIntArray methodDocRanges;
50
	
51
	private int[] fieldAnchorIndexes;
52
	private int fieldAnchorIndexesCount;
53
	private int fieldLastAnchorFoundIndex;
54
	private int[] methodAnchorIndexes;
55
	private int methodAnchorIndexesCount;
56
	private int methodLastAnchorFoundIndex;
57
	private int[] unknownFormatAnchorIndexes;
58
	private int unknownFormatAnchorIndexesCount;
59
	private int unknownFormatLastAnchorFoundIndex;
60
	private int[] tempAnchorIndexes;
61
	private int tempAnchorIndexesCount;
62
	private int tempLastAnchorFoundIndex;
63
	
64
	public JavadocContents(BinaryType type, String content) {
65
		this.type = type;
66
		this.content = content;
67
	}
68
	
69
	/*
70
	 * Return the full content of the javadoc
71
	 */
72
	public String getContent() {
73
		return this.content;
74
	}
75
	
76
	/*
77
	 * Returns the part of the javadoc that describe the type
78
	 */
79
	public String getTypeDoc() throws JavaModelException {
80
		if (this.content == null) return null;
81
		
82
		synchronized (this) {
83
			if (this.typeDocRange == null) {
84
				computeTypeRange();
85
			}
86
		}
87
		
88
		if (this.typeDocRange != null) {
89
			if (this.typeDocRange == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, this.type));
90
			return this.content.substring(this.typeDocRange[0], this.typeDocRange[1]);
91
		}
92
		return null;
93
	}
94
	
95
	/*
96
	 * Returns the part of the javadoc that describe a field of the type
97
	 */
98
	public String getFieldDoc(IField child) throws JavaModelException {
99
		if (this.content == null) return null;
100
		
101
		int[] range = null;
102
		synchronized (this) {
103
			if (this.fieldDocRanges == null) {
104
				this.fieldDocRanges = new HashtableOfObjectToIntArray();
105
			} else {
106
				range = this.fieldDocRanges.get(child);
107
			}
108
			
109
			if (range == null) {
110
				range = computeFieldRange(child);
111
				this.fieldDocRanges.put(child, range);
112
			}
113
		}
114
		
115
		if (range != null) {
116
			if (range == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, child));
117
			return this.content.substring(range[0], range[1]);
118
		}
119
		return null;
120
	}
121
	
122
	/*
123
	 * Returns the part of the javadoc that describe a method of the type
124
	 */
125
	public String getMethodDoc(IMethod child) throws JavaModelException {
126
		if (this.content == null) return null;
127
		
128
		int[] range = null;
129
		synchronized (this) {
130
			if (this.methodDocRanges == null) {
131
				this.methodDocRanges = new HashtableOfObjectToIntArray();
132
			} else {
133
				range = this.methodDocRanges.get(child);
134
			}
135
			
136
			if (range == null) {
137
				range = computeMethodRange(child);
138
				this.methodDocRanges.put(child, range);
139
			}
140
		}
141
		
142
		if (range != null) {
143
			if (range == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, child));
144
			return this.content.substring(range[0], range[1]);
145
		}
146
		return null;
147
	}
148
	
149
	/*
150
	 * Compute the ranges of the parts of the javadoc that describe each method of the type
151
	 */
152
	private int[] computeChildRange(String anchor, int indexOfSectionBottom) throws JavaModelException {
153
		
154
		// checks each known anchor locations
155
		if (this.tempAnchorIndexesCount > 0) {
156
			for (int i = 0; i < this.tempAnchorIndexesCount; i++) {
157
				int anchorEndStart = this.tempAnchorIndexes[i];
158
				
159
				if (anchorEndStart != -1 && this.content.startsWith(anchor, anchorEndStart)) {
160
					
161
					this.tempAnchorIndexes[i] = -1;
162
					
163
					return computeChildRange(anchorEndStart, anchor, indexOfSectionBottom);
164
				}
165
			}
166
		}
167
		
168
		int fromIndex = this.tempLastAnchorFoundIndex;
169
		int index;
170
		
171
		// check each next unknown anchor locations
172
		while ((index = this.content.indexOf(JavadocConstants.ANCHOR_PREFIX_START, fromIndex)) != -1 && (index < indexOfSectionBottom || indexOfSectionBottom == -1)) {
173
			fromIndex = index + 1;
174
			
175
			int anchorEndStart = index + JavadocConstants.ANCHOR_PREFIX_START_LENGHT;
176
			
177
			this.tempLastAnchorFoundIndex = anchorEndStart;
178
			
179
			if (this.content.startsWith(anchor, anchorEndStart)) {
180
				
181
				return computeChildRange(anchorEndStart, anchor, indexOfSectionBottom);
182
			} else {
183
				if (this.tempAnchorIndexes.length == this.tempAnchorIndexesCount) {
184
					System.arraycopy(this.tempAnchorIndexes, 0, this.tempAnchorIndexes = new int[this.tempAnchorIndexesCount + 20], 0, this.tempAnchorIndexesCount);
185
				}
186
				
187
				this.tempAnchorIndexes[this.tempAnchorIndexesCount++] = anchorEndStart;
188
			}
189
		}
190
		
191
		return null;
192
	}
193
	
194
	private int[] computeChildRange(int anchorEndStart, String anchor, int indexOfBottom) {
195
		int[] range = null;
196
				
197
		// try to find the bottom of the section
198
		if (indexOfBottom != -1) {
199
			// try to find the end of the anchor
200
			int indexOfEndLink = this.content.indexOf(JavadocConstants.ANCHOR_SUFFIX, anchorEndStart + anchor.length());
201
			if (indexOfEndLink != -1) {
202
				// try to find the next anchor
203
				int indexOfNextElement = this.content.indexOf(JavadocConstants.ANCHOR_PREFIX_START, indexOfEndLink);
204
				
205
				int javadocStart = indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH;
206
				int javadocEnd = indexOfNextElement == -1 ? indexOfBottom : Math.min(indexOfNextElement, indexOfBottom);
207
				range = new int[]{javadocStart, javadocEnd};
208
			} else {
209
				// the anchor has no suffix
210
				range = UNKNOWN_FORMAT;
211
			}
212
		} else {
213
			// the detail section has no bottom
214
			range = UNKNOWN_FORMAT;
215
		}
216
		
217
		return range;
218
	}
219
220
	private void computeChildrenSections() {
221
		// try to find the next separator part
222
		int lastIndex = this.content.indexOf(JavadocConstants.SEPARATOR_START, this.childrenStart);
223
		
224
		// try to find field detail start
225
		this.indexOfFieldDetails = this.content.indexOf(JavadocConstants.FIELD_DETAIL, lastIndex);
226
		lastIndex = this.indexOfFieldDetails == -1 ? lastIndex : this.indexOfFieldDetails;
227
		
228
		// try to find constructor detail start
229
		this.indexOfConstructorDetails = this.content.indexOf(JavadocConstants.CONSTRUCTOR_DETAIL, lastIndex);
230
		lastIndex = this.indexOfConstructorDetails == -1 ? lastIndex : this.indexOfConstructorDetails;
231
		
232
		// try to find method detail start
233
		this.indexOfMethodDetails = this.content.indexOf(JavadocConstants.METHOD_DETAIL, lastIndex);
234
		lastIndex = this.indexOfMethodDetails == -1 ? lastIndex : this.indexOfMethodDetails;
235
		
236
		// we take the end of class data
237
		this.indexOfEndOfClassData = this.content.indexOf(JavadocConstants.END_OF_CLASS_DATA, lastIndex);
238
		
239
		// try to find the field detail end
240
		this.indexOfFieldsBottom =
241
			this.indexOfConstructorDetails != -1 ? this.indexOfConstructorDetails :
242
				this.indexOfMethodDetails != -1 ? this.indexOfMethodDetails:
243
					this.indexOfEndOfClassData;
244
		
245
		// try to find the constructor detail end
246
		this.indexOfConstructorsBottom =
247
			this.indexOfMethodDetails != -1 ? this.indexOfMethodDetails:
248
				this.indexOfEndOfClassData;
249
		
250
		// try to find the method detail end
251
		this.indexOfMethodsBottom = this.indexOfEndOfClassData;
252
		
253
		this.indexOfAllMethodsTop =
254
			this.indexOfConstructorDetails != -1 ?
255
					this.indexOfConstructorDetails :
256
						this.indexOfMethodDetails;
257
		
258
		this.indexOfAllMethodsBottom = this.indexOfMethodsBottom;
259
	
260
		this.hasComputedChildrenSections = true;
261
	}
262
263
	/*
264
	 * Compute the ranges of the parts of the javadoc that describe each child of the type (fields, methods)
265
	 */
266
	private int[] computeFieldRange(IField field) throws JavaModelException {
267
		if (!this.hasComputedChildrenSections) {
268
			computeChildrenSections();
269
		}
270
		
271
		String anchor = field.getElementName() + JavadocConstants.ANCHOR_PREFIX_END;
272
		
273
		int[] range = null;
274
		
275
		if (this.indexOfFieldDetails == -1 || this.indexOfFieldsBottom == -1) {
276
			// the detail section has no top or bottom, so the doc has an unknown format
277
			if (this.unknownFormatAnchorIndexes == null) {
278
				this.unknownFormatAnchorIndexes = new int[this.type.getChildren().length];
279
				this.unknownFormatAnchorIndexesCount = 0;
280
				this.unknownFormatLastAnchorFoundIndex = this.childrenStart;
281
			}
282
			
283
			this.tempAnchorIndexes = this.unknownFormatAnchorIndexes;
284
			this.tempAnchorIndexesCount = this.unknownFormatAnchorIndexesCount;
285
			this.tempLastAnchorFoundIndex = this.unknownFormatLastAnchorFoundIndex;
286
			
287
			range = computeChildRange(anchor, this.indexOfFieldsBottom);
288
			
289
			this.unknownFormatLastAnchorFoundIndex = this.tempLastAnchorFoundIndex;
290
			this.unknownFormatAnchorIndexesCount = this.tempAnchorIndexesCount;
291
			this.unknownFormatAnchorIndexes = this.tempAnchorIndexes;
292
		} else {
293
			if (this.fieldAnchorIndexes == null) {
294
				this.fieldAnchorIndexes = new int[this.type.getFields().length];
295
				this.fieldAnchorIndexesCount = 0;
296
				this.fieldLastAnchorFoundIndex = this.indexOfFieldDetails;
297
			}
298
			
299
			this.tempAnchorIndexes = this.fieldAnchorIndexes;
300
			this.tempAnchorIndexesCount = this.fieldAnchorIndexesCount;
301
			this.tempLastAnchorFoundIndex = this.fieldLastAnchorFoundIndex;
302
			
303
			range = computeChildRange(anchor, this.indexOfFieldsBottom);
304
			
305
			this.fieldLastAnchorFoundIndex = this.tempLastAnchorFoundIndex;
306
			this.fieldAnchorIndexesCount = this.tempAnchorIndexesCount;
307
			this.fieldAnchorIndexes = this.tempAnchorIndexes;
308
		}
309
		
310
		return range;
311
	}
312
	
313
	/*
314
	 * Compute the ranges of the parts of the javadoc that describe each method of the type
315
	 */
316
	private int[] computeMethodRange(IMethod method) throws JavaModelException {
317
		if (!this.hasComputedChildrenSections) {
318
			computeChildrenSections();
319
		}
320
		
321
		String anchor = computeMethodAnchorPrefixEnd((BinaryMethod)method);
322
		
323
		int[] range = null;
324
		
325
		if (this.indexOfAllMethodsTop == -1 || this.indexOfAllMethodsBottom == -1) {
326
			// the detail section has no top or bottom, so the doc has an unknown format
327
			if (this.unknownFormatAnchorIndexes == null) {
328
				this.unknownFormatAnchorIndexes = new int[this.type.getChildren().length];
329
				this.unknownFormatAnchorIndexesCount = 0;
330
				this.unknownFormatLastAnchorFoundIndex = this.childrenStart;
331
			}
332
			
333
			this.tempAnchorIndexes = this.unknownFormatAnchorIndexes;
334
			this.tempAnchorIndexesCount = this.unknownFormatAnchorIndexesCount;
335
			this.tempLastAnchorFoundIndex = this.unknownFormatLastAnchorFoundIndex;
336
			
337
			range = computeChildRange(anchor, this.indexOfFieldsBottom);
338
			
339
			this.unknownFormatLastAnchorFoundIndex = this.tempLastAnchorFoundIndex;
340
			this.unknownFormatAnchorIndexesCount = this.tempAnchorIndexesCount;
341
			this.unknownFormatAnchorIndexes = this.tempAnchorIndexes;
342
		} else {			
343
			if (this.methodAnchorIndexes == null) {
344
				this.methodAnchorIndexes = new int[this.type.getFields().length];
345
				this.methodAnchorIndexesCount = 0;
346
				this.methodLastAnchorFoundIndex = this.indexOfAllMethodsTop;
347
			}
348
			
349
			this.tempAnchorIndexes = this.methodAnchorIndexes;
350
			this.tempAnchorIndexesCount = this.methodAnchorIndexesCount;
351
			this.tempLastAnchorFoundIndex = this.methodLastAnchorFoundIndex;
352
			
353
			range = computeChildRange(anchor, this.indexOfAllMethodsBottom);
354
			
355
			this.methodLastAnchorFoundIndex = this.tempLastAnchorFoundIndex;
356
			this.methodAnchorIndexesCount = this.tempAnchorIndexesCount;
357
			this.methodAnchorIndexes = this.tempAnchorIndexes;
358
		}
359
		
360
		return range;
361
	}
362
	
363
	private String computeMethodAnchorPrefixEnd(BinaryMethod method) throws JavaModelException {
364
		String typeQualifiedName = null;
365
		if (this.type.isMember()) {
366
			IType currentType = this.type;
367
			StringBuffer buffer = new StringBuffer();
368
			while (currentType != null) {
369
				buffer.insert(0, currentType.getElementName());
370
				currentType = currentType.getDeclaringType();
371
				if (currentType != null) {
372
					buffer.insert(0, '.');
373
				}
374
			}
375
			typeQualifiedName = new String(buffer.toString());
376
		} else {
377
			typeQualifiedName = this.type.getElementName();
378
		}
379
		
380
		String methodName = method.getElementName();
381
		if (method.isConstructor()) {
382
			methodName = typeQualifiedName;
383
		}
384
		IBinaryMethod info = (IBinaryMethod) method.getElementInfo();
385
386
		char[] genericSignature = info.getGenericSignature();
387
		String anchor = null;
388
		if (genericSignature != null) {
389
			genericSignature = CharOperation.replaceOnCopy(genericSignature, '/', '.');
390
			anchor = Util.toAnchor(0, genericSignature, methodName, Flags.isVarargs(method.getFlags()));
391
			if (anchor == null) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, method));
392
		} else {
393
			anchor = Signature.toString(method.getSignature().replace('/', '.'), methodName, null, true, false, Flags.isVarargs(method.getFlags()));
394
		}
395
		IType declaringType = this.type;
396
		if (declaringType.isMember()) {
397
			int depth = 0;
398
			final String packageFragmentName = declaringType.getPackageFragment().getElementName();
399
			// might need to remove a part of the signature corresponding to the synthetic argument
400
			final IJavaProject javaProject = declaringType.getJavaProject();
401
			char[][] typeNames = CharOperation.splitOn('.', typeQualifiedName.toCharArray());
402
			if (!Flags.isStatic(declaringType.getFlags())) depth++;
403
			StringBuffer typeName = new StringBuffer();
404
			for (int i = 0, max = typeNames.length; i < max; i++) {
405
				if (typeName.length() == 0) {
406
					typeName.append(typeNames[i]);
407
				} else {
408
					typeName.append('.').append(typeNames[i]);
409
				}
410
				IType resolvedType = javaProject.findType(packageFragmentName, String.valueOf(typeName));
411
				if (resolvedType != null && resolvedType.isMember() && !Flags.isStatic(resolvedType.getFlags())) depth++;
412
			}
413
			if (depth != 0) {
414
				int indexOfOpeningParen = anchor.indexOf('(');
415
				if (indexOfOpeningParen == -1) return null;
416
				int index = indexOfOpeningParen;
417
				indexOfOpeningParen++;
418
				for (int i = 0; i < depth; i++) {
419
					int indexOfComma = anchor.indexOf(',', index);
420
					if (indexOfComma != -1) {
421
						index = indexOfComma + 2;
422
					}
423
				}
424
				anchor = anchor.substring(0, indexOfOpeningParen) + anchor.substring(index);
425
			}
426
		}
427
		return anchor + JavadocConstants.ANCHOR_PREFIX_END;
428
	}
429
	
430
	/*
431
	 * Compute the range of the part of the javadoc that describe the type
432
	 */
433
	private void computeTypeRange() throws JavaModelException {
434
		final int indexOfStartOfClassData = this.content.indexOf(JavadocConstants.START_OF_CLASS_DATA);
435
		if (indexOfStartOfClassData == -1) {
436
			this.typeDocRange = UNKNOWN_FORMAT;
437
			return;
438
		}
439
		int indexOfNextSeparator = this.content.indexOf(JavadocConstants.SEPARATOR_START, indexOfStartOfClassData);
440
		if (indexOfNextSeparator == -1) {
441
			this.typeDocRange = UNKNOWN_FORMAT;
442
			return;
443
		}
444
		int indexOfNextSummary = this.content.indexOf(JavadocConstants.NESTED_CLASS_SUMMARY, indexOfNextSeparator);
445
		if (indexOfNextSummary == -1 && this.type.isEnum()) {
446
			// try to find enum constant summary start
447
			indexOfNextSummary = this.content.indexOf(JavadocConstants.ENUM_CONSTANT_SUMMARY, indexOfNextSeparator);
448
		}
449
		if (indexOfNextSummary == -1 && this.type.isAnnotation()) {
450
			// try to find required enum constant summary start
451
			indexOfNextSummary = this.content.indexOf(JavadocConstants.ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY, indexOfNextSeparator);
452
			if (indexOfNextSummary == -1) {
453
				// try to find optional enum constant summary start
454
				indexOfNextSummary = this.content.indexOf(JavadocConstants.ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY, indexOfNextSeparator);
455
			}
456
		}
457
		if (indexOfNextSummary == -1) {
458
			// try to find field summary start
459
			indexOfNextSummary = this.content.indexOf(JavadocConstants.FIELD_SUMMARY, indexOfNextSeparator);
460
		}
461
		if (indexOfNextSummary == -1) {
462
			// try to find constructor summary start
463
			indexOfNextSummary = this.content.indexOf(JavadocConstants.CONSTRUCTOR_SUMMARY, indexOfNextSeparator);
464
		}
465
		if (indexOfNextSummary == -1) {
466
			// try to find method summary start
467
			indexOfNextSummary = this.content.indexOf(JavadocConstants.METHOD_SUMMARY, indexOfNextSeparator);
468
		}
469
		
470
		if (indexOfNextSummary == -1) {
471
			// we take the end of class data
472
			indexOfNextSummary = this.content.indexOf(JavadocConstants.END_OF_CLASS_DATA, indexOfNextSeparator);
473
		} else {
474
			// improve performance of computation of children ranges
475
			this.childrenStart = indexOfNextSummary + 1;
476
		}
477
		
478
		if (indexOfNextSummary == -1) {
479
			this.typeDocRange = UNKNOWN_FORMAT;
480
			return;
481
		}
482
		/*
483
		 * Check out to cut off the hierarchy see 119844
484
		 * We remove what the contents between the start of class data and the first <P>
485
		 */
486
		int start = indexOfStartOfClassData + JavadocConstants.START_OF_CLASS_DATA_LENGTH;
487
		int indexOfFirstParagraph = this.content.indexOf("<P>", start); //$NON-NLS-1$
488
		if (indexOfFirstParagraph == -1) {
489
			indexOfFirstParagraph = this.content.indexOf("<p>", start); //$NON-NLS-1$
490
		}
491
		if (indexOfFirstParagraph != -1 && indexOfFirstParagraph < indexOfNextSummary) {
492
			start = indexOfFirstParagraph;
493
		}
494
		
495
		this.typeDocRange = new int[]{start, indexOfNextSummary};
496
	}
497
}

Return to bug 237241