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.IJavaElement; |
15 |
import org.eclipse.jdt.core.IJavaModelStatusConstants; |
16 |
import org.eclipse.jdt.core.IJavaProject; |
17 |
import org.eclipse.jdt.core.IType; |
18 |
import org.eclipse.jdt.core.JavaModelException; |
19 |
import org.eclipse.jdt.core.Signature; |
20 |
import org.eclipse.jdt.core.compiler.CharOperation; |
21 |
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; |
22 |
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToIntArray; |
23 |
import org.eclipse.jdt.internal.core.util.Util; |
24 |
|
25 |
public class JavadocContents { |
26 |
private static final int[] UNKNOWN_FORMAT = new int[0]; |
27 |
|
28 |
private BinaryType type; |
29 |
private String content; |
30 |
|
31 |
private int childrenStart; |
32 |
|
33 |
private int[] typeDocRange; |
34 |
private HashtableOfObjectToIntArray childrenDocRanges; |
35 |
|
36 |
public JavadocContents(BinaryType type, String content) { |
37 |
this.type = type; |
38 |
this.content = content; |
39 |
} |
40 |
|
41 |
/* |
42 |
* Return the full content of the javadoc |
43 |
*/ |
44 |
public String getContent() { |
45 |
return this.content; |
46 |
} |
47 |
|
48 |
/* |
49 |
* Returns the part of the javadoc that describe the type |
50 |
*/ |
51 |
public String getTypeDoc() throws JavaModelException { |
52 |
if (this.content == null) return null; |
53 |
|
54 |
synchronized (this) { |
55 |
if (this.typeDocRange == null) { |
56 |
this.computeTypeRange(); |
57 |
} |
58 |
} |
59 |
|
60 |
if (typeDocRange != null) { |
61 |
if (typeDocRange == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, type)); |
62 |
return this.content.substring(typeDocRange[0], typeDocRange[1]); |
63 |
} |
64 |
return null; |
65 |
} |
66 |
|
67 |
/* |
68 |
* Returns the part of the javadoc that describe a child of the type (field, method) |
69 |
*/ |
70 |
public String getChildDoc(IJavaElement child) throws JavaModelException { |
71 |
if (this.content == null) return null; |
72 |
|
73 |
synchronized (this) { |
74 |
if (this.childrenDocRanges == null) { |
75 |
this.computeChildrenRanges(); |
76 |
} |
77 |
} |
78 |
|
79 |
int[] range = this.childrenDocRanges.get(child); |
80 |
if (range != null) { |
81 |
if (range == UNKNOWN_FORMAT) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, child)); |
82 |
return this.content.substring(range[0], range[1]); |
83 |
} |
84 |
return null; |
85 |
} |
86 |
|
87 |
/* |
88 |
* Compute the ranges of the parts of the javadoc that describe each child of the type (fields, methods) |
89 |
*/ |
90 |
private void computeChildrenRanges() throws JavaModelException { |
91 |
this.childrenDocRanges = new HashtableOfObjectToIntArray(); |
92 |
|
93 |
IJavaElement[] children = this.type.getChildren(); |
94 |
|
95 |
int length = children.length; |
96 |
if (length > 0) { |
97 |
String typeQualifiedName = null; |
98 |
if (type.isMember()) { |
99 |
IType currentType = type; |
100 |
StringBuffer buffer = new StringBuffer(); |
101 |
while (currentType != null) { |
102 |
buffer.insert(0, currentType.getElementName()); |
103 |
currentType = currentType.getDeclaringType(); |
104 |
if (currentType != null) { |
105 |
buffer.insert(0, '.'); |
106 |
} |
107 |
} |
108 |
typeQualifiedName = new String(buffer.toString()); |
109 |
} else { |
110 |
typeQualifiedName = type.getElementName(); |
111 |
} |
112 |
|
113 |
// try to find the next separator part |
114 |
int lastIndex = content.indexOf(JavadocConstants.SEPARATOR_START, this.childrenStart); |
115 |
|
116 |
// try to find constructor detail start |
117 |
int indexOfConstructorDetails = content.indexOf(JavadocConstants.CONSTRUCTOR_DETAIL, lastIndex); |
118 |
lastIndex = indexOfConstructorDetails == -1 ? lastIndex : indexOfConstructorDetails; |
119 |
|
120 |
// try to find method detail start |
121 |
int indexOfMethodDetails = content.indexOf(JavadocConstants.METHOD_DETAIL, lastIndex); |
122 |
lastIndex = indexOfMethodDetails == -1 ? lastIndex : indexOfMethodDetails; |
123 |
|
124 |
// we take the end of class data |
125 |
int indexOfEndOfClassData = content.indexOf(JavadocConstants.END_OF_CLASS_DATA, lastIndex); |
126 |
|
127 |
// try to find the field detail end |
128 |
int indexOfFieldsBottom = |
129 |
indexOfConstructorDetails != -1 ? indexOfConstructorDetails : |
130 |
indexOfMethodDetails != -1 ? indexOfMethodDetails: |
131 |
indexOfEndOfClassData; |
132 |
|
133 |
// try to find the constructor detail end |
134 |
int indexOfConstructorsBottom = |
135 |
indexOfMethodDetails != -1 ? indexOfMethodDetails: |
136 |
indexOfEndOfClassData; |
137 |
|
138 |
// try to find the method detail end |
139 |
int indexOfMethodsBottom = indexOfEndOfClassData; |
140 |
|
141 |
String[] anchors = new String[length]; |
142 |
int anchorCount = 0; |
143 |
|
144 |
// compute the end of the anchor prefix of each children |
145 |
for (int i = 0; i < length; i++) { |
146 |
IJavaElement child = children[i]; |
147 |
switch (child.getElementType()) { |
148 |
case IJavaElement.METHOD: |
149 |
anchors[anchorCount] = computeMethodAnchorPrefixEnd((BinaryMethod)child, typeQualifiedName); |
150 |
children[anchorCount] = child; |
151 |
anchorCount++; |
152 |
break; |
153 |
case IJavaElement.FIELD: |
154 |
anchors[anchorCount] = child.getElementName() + JavadocConstants.ANCHOR_PREFIX_END; |
155 |
children[anchorCount] = child; |
156 |
anchorCount++; |
157 |
break; |
158 |
} |
159 |
} |
160 |
|
161 |
int fromIndex = 0; |
162 |
int index; |
163 |
|
164 |
// check each anchor in the javadoc |
165 |
while ((index = content.indexOf(JavadocConstants.ANCHOR_PREFIX_START, fromIndex)) != -1) { |
166 |
fromIndex = index + 1; |
167 |
|
168 |
int anchorEndStart = index + JavadocConstants.ANCHOR_PREFIX_START_LENGHT; |
169 |
|
170 |
// check if this anchor is an anchor of a known child |
171 |
done : for (int i = 0; i < anchorCount; i++) { |
172 |
String anchor = anchors[i]; |
173 |
IJavaElement child = children[i]; |
174 |
|
175 |
if (anchor != null && content.startsWith(anchor, anchorEndStart)) { |
176 |
|
177 |
// the child corresponding to the anchor is found. |
178 |
// the anchor is removed from the list of not found anchors |
179 |
anchors[i] = null; |
180 |
|
181 |
// try to find the bottom of the section |
182 |
int indexOfBottom = -1; |
183 |
switch (child.getElementType()) { |
184 |
case IJavaElement.METHOD: |
185 |
indexOfBottom = ((BinaryMethod)child).isConstructor() ? indexOfConstructorsBottom : indexOfMethodsBottom; |
186 |
break; |
187 |
case IJavaElement.FIELD: |
188 |
indexOfBottom = indexOfFieldsBottom; |
189 |
break; |
190 |
} |
191 |
|
192 |
if (indexOfBottom != -1) { |
193 |
// try to find the end of the anchor |
194 |
int indexOfEndLink = content.indexOf(JavadocConstants.ANCHOR_SUFFIX, anchorEndStart + anchor.length()); |
195 |
if (indexOfEndLink != -1) { |
196 |
// try to find the next anchor |
197 |
int indexOfNextElement = content.indexOf(JavadocConstants.ANCHOR_PREFIX_START, indexOfEndLink); |
198 |
|
199 |
int javadocStart = indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH; |
200 |
int javadocEnd = indexOfNextElement == -1 ? indexOfBottom : Math.min(indexOfNextElement, indexOfBottom); |
201 |
this.childrenDocRanges.put(child, new int[]{javadocStart, javadocEnd}); |
202 |
} else { |
203 |
// the anchor has no suffix |
204 |
this.childrenDocRanges.put(child, UNKNOWN_FORMAT); |
205 |
} |
206 |
} else { |
207 |
// the detail section has no bottom |
208 |
this.childrenDocRanges.put(child, UNKNOWN_FORMAT); |
209 |
} |
210 |
break done; |
211 |
} |
212 |
} |
213 |
} |
214 |
} |
215 |
} |
216 |
|
217 |
private String computeMethodAnchorPrefixEnd(BinaryMethod method, String typeQualifiedName) throws JavaModelException { |
218 |
String methodName = method.getElementName(); |
219 |
if (method.isConstructor()) { |
220 |
methodName = typeQualifiedName; |
221 |
} |
222 |
IBinaryMethod info = (IBinaryMethod) method.getElementInfo(); |
223 |
|
224 |
char[] genericSignature = info.getGenericSignature(); |
225 |
String anchor = null; |
226 |
if (genericSignature != null) { |
227 |
genericSignature = CharOperation.replaceOnCopy(genericSignature, '/', '.'); |
228 |
anchor = Util.toAnchor(genericSignature, methodName, Flags.isVarargs(method.getFlags())); |
229 |
if (anchor == null) throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.UNKNOWN_JAVADOC_FORMAT, method)); |
230 |
} else { |
231 |
anchor = Signature.toString(method.getSignature().replace('/', '.'), methodName, null, true, false, Flags.isVarargs(method.getFlags())); |
232 |
} |
233 |
IType declaringType = this.type; |
234 |
if (declaringType.isMember()) { |
235 |
int depth = 0; |
236 |
final String packageFragmentName = declaringType.getPackageFragment().getElementName(); |
237 |
// might need to remove a part of the signature corresponding to the synthetic argument |
238 |
final IJavaProject javaProject = declaringType.getJavaProject(); |
239 |
char[][] typeNames = CharOperation.splitOn('.', typeQualifiedName.toCharArray()); |
240 |
if (!Flags.isStatic(declaringType.getFlags())) depth++; |
241 |
StringBuffer typeName = new StringBuffer(); |
242 |
for (int i = 0, max = typeNames.length; i < max; i++) { |
243 |
if (typeName.length() == 0) { |
244 |
typeName.append(typeNames[i]); |
245 |
} else { |
246 |
typeName.append('.').append(typeNames[i]); |
247 |
} |
248 |
IType resolvedType = javaProject.findType(packageFragmentName, String.valueOf(typeName)); |
249 |
if (resolvedType != null && resolvedType.isMember() && !Flags.isStatic(resolvedType.getFlags())) depth++; |
250 |
} |
251 |
if (depth != 0) { |
252 |
int indexOfOpeningParen = anchor.indexOf('('); |
253 |
if (indexOfOpeningParen == -1) return null; |
254 |
int index = indexOfOpeningParen; |
255 |
indexOfOpeningParen++; |
256 |
for (int i = 0; i < depth; i++) { |
257 |
int indexOfComma = anchor.indexOf(',', index); |
258 |
if (indexOfComma != -1) { |
259 |
index = indexOfComma + 2; |
260 |
} |
261 |
} |
262 |
anchor = anchor.substring(0, indexOfOpeningParen) + anchor.substring(index); |
263 |
} |
264 |
} |
265 |
return anchor + JavadocConstants.ANCHOR_PREFIX_END; |
266 |
} |
267 |
|
268 |
/* |
269 |
* Compute the range of the part of the javadoc that describe the type |
270 |
*/ |
271 |
private void computeTypeRange() throws JavaModelException { |
272 |
final int indexOfStartOfClassData = content.indexOf(JavadocConstants.START_OF_CLASS_DATA); |
273 |
if (indexOfStartOfClassData == -1) { |
274 |
this.typeDocRange = UNKNOWN_FORMAT; |
275 |
return; |
276 |
} |
277 |
int indexOfNextSeparator = content.indexOf(JavadocConstants.SEPARATOR_START, indexOfStartOfClassData); |
278 |
if (indexOfNextSeparator == -1) { |
279 |
this.typeDocRange = UNKNOWN_FORMAT; |
280 |
return; |
281 |
} |
282 |
int indexOfNextSummary = content.indexOf(JavadocConstants.NESTED_CLASS_SUMMARY, indexOfNextSeparator); |
283 |
if (indexOfNextSummary == -1 && type.isEnum()) { |
284 |
// try to find enum constant summary start |
285 |
indexOfNextSummary = content.indexOf(JavadocConstants.ENUM_CONSTANT_SUMMARY, indexOfNextSeparator); |
286 |
} |
287 |
if (indexOfNextSummary == -1 && type.isAnnotation()) { |
288 |
// try to find required enum constant summary start |
289 |
indexOfNextSummary = content.indexOf(JavadocConstants.ANNOTATION_TYPE_REQUIRED_MEMBER_SUMMARY, indexOfNextSeparator); |
290 |
if (indexOfNextSummary == -1) { |
291 |
// try to find optional enum constant summary start |
292 |
indexOfNextSummary = content.indexOf(JavadocConstants.ANNOTATION_TYPE_OPTIONAL_MEMBER_SUMMARY, indexOfNextSeparator); |
293 |
} |
294 |
} |
295 |
if (indexOfNextSummary == -1) { |
296 |
// try to find field summary start |
297 |
indexOfNextSummary = content.indexOf(JavadocConstants.FIELD_SUMMARY, indexOfNextSeparator); |
298 |
} |
299 |
if (indexOfNextSummary == -1) { |
300 |
// try to find constructor summary start |
301 |
indexOfNextSummary = content.indexOf(JavadocConstants.CONSTRUCTOR_SUMMARY, indexOfNextSeparator); |
302 |
} |
303 |
if (indexOfNextSummary == -1) { |
304 |
// try to find method summary start |
305 |
indexOfNextSummary = content.indexOf(JavadocConstants.METHOD_SUMMARY, indexOfNextSeparator); |
306 |
} |
307 |
|
308 |
if (indexOfNextSummary == -1) { |
309 |
// we take the end of class data |
310 |
indexOfNextSummary = content.indexOf(JavadocConstants.END_OF_CLASS_DATA, indexOfNextSeparator); |
311 |
} else { |
312 |
// improve performance of computation of children ranges |
313 |
this.childrenStart = indexOfNextSummary + 1; |
314 |
} |
315 |
|
316 |
if (indexOfNextSummary == -1) { |
317 |
this.typeDocRange = UNKNOWN_FORMAT; |
318 |
return; |
319 |
} |
320 |
/* |
321 |
* Check out to cut off the hierarchy see 119844 |
322 |
* We remove what the contents between the start of class data and the first <P> |
323 |
*/ |
324 |
int start = indexOfStartOfClassData + JavadocConstants.START_OF_CLASS_DATA_LENGTH; |
325 |
int indexOfFirstParagraph = content.indexOf("<P>", start); //$NON-NLS-1$ |
326 |
if (indexOfFirstParagraph == -1) { |
327 |
indexOfFirstParagraph = content.indexOf("<p>", start); //$NON-NLS-1$ |
328 |
} |
329 |
if (indexOfFirstParagraph != -1 && indexOfFirstParagraph < indexOfNextSummary) { |
330 |
start = indexOfFirstParagraph; |
331 |
} |
332 |
|
333 |
this.typeDocRange = new int[]{start, indexOfNextSummary}; |
334 |
} |
335 |
} |