2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 import java.util.regex.Pattern;
18 import java.util.regex.Matcher;
19 import java.util.ArrayList;
22 * Class that represents what you see in an link or see tag. This is
23 * factored out of SeeTagInfo so it can be used elsewhere (like AttrTagInfo).
25 public class LinkReference {
27 /** The original text. */
30 /** The kind of this tag, if we have a new suggestion after parsing. */
33 /** The user visible text. */
39 /** The {@link PackageInfo} if any. */
40 public PackageInfo packageInfo;
42 /** The {@link ClassInfo} if any. */
43 public ClassInfo classInfo;
45 /** The {@link MemberInfo} if any. */
46 public MemberInfo memberInfo;
48 /** The name of the referenced member PackageInfo} if any. */
49 public String referencedMemberName;
51 /** Set to true if everything is a-ok */
55 * regex pattern to use when matching explicit "<a href" reference text
57 private static final Pattern HREF_PATTERN
58 = Pattern.compile("^<a href=\"([^\"]*)\">([^<]*)</a>[ \n\r\t]*$",
59 Pattern.CASE_INSENSITIVE);
62 * regex pattern to use when matching double-quoted reference text
64 private static final Pattern QUOTE_PATTERN
65 = Pattern.compile("^\"([^\"]*)\"[ \n\r\t]*$");
68 * Parse and resolve a link string.
70 * @param text the original text
71 * @param base the class or whatever that this link is on
72 * @param pos the original position in the source document
73 * @return a new link reference. It always returns something. If there was an
74 * error, it logs it and fills in href and label with error text.
76 public static LinkReference parse(String text, ContainerInfo base, SourcePositionInfo pos,
77 boolean printOnErrors) {
78 LinkReference result = new LinkReference();
82 int len = text.length();
87 for (index=0; index<len; index++) {
88 char c = text.charAt(index);
119 if (index == len && pairs != 0) {
120 Errors.error(Errors.UNRESOLVED_LINK, pos,
121 "unable to parse link/see tag: " + text.trim());
127 for (; index<len; index++) {
128 char c = text.charAt(index);
129 if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
134 result.label = text.substring(index);
140 mem = text.substring(1, linkend);
142 else if (pound > 0) {
143 ref = text.substring(0, pound);
144 mem = text.substring(pound+1, linkend);
147 ref = text.substring(0, linkend);
151 // parse parameters, if any
152 String[] params = null;
153 String[] paramDimensions = null;
155 index = mem.indexOf('(');
157 ArrayList<String> paramList = new ArrayList<String>();
158 ArrayList<String> paramDimensionList = new ArrayList<String>();
169 for (int i=start; i<len; i++) {
170 char c = mem.charAt(i);
174 if (c!=' ' && c!='\t' && c!='\r' && c!='\n') {
190 else if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
196 if (typeend >= 0 || c == ')' || c == ',') {
200 String s = mem.substring(typestart, typeend);
203 for (int j=0; j<dimension; j++) {
206 paramDimensionList.add(s);
210 if (c == ',' || c == ')') {
219 if (c == ',' || c == ')') {
226 params = paramList.toArray(new String[paramList.size()]);
227 paramDimensions = paramDimensionList.toArray(new String[paramList.size()]);
228 mem = mem.substring(0, index);
233 if (base instanceof ClassInfo) {
234 cl = (ClassInfo)base;
238 // no class or package was provided, assume it's this class
240 result.classInfo = cl;
243 // they provided something, maybe it's a class or a package
245 result.classInfo = cl.extendedFindClass(ref);
246 if (result.classInfo == null) {
247 result.classInfo = cl.findClass(ref);
249 if (result.classInfo == null) {
250 result.classInfo = cl.findInnerClass(ref);
253 if (result.classInfo == null) {
254 result.classInfo = Converter.obtainClass(ref);
256 if (result.classInfo == null) {
257 result.packageInfo = Converter.obtainPackage(ref);
261 if (result.classInfo != null && mem != null) {
262 // it's either a field or a method, prefer a field
263 if (params == null) {
264 FieldInfo field = result.classInfo.findField(mem);
265 // findField looks in containing classes, so it might actually
266 // be somewhere else; link to where it really is, not what they
269 result.classInfo = field.containingClass();
270 result.memberInfo = field;
273 if (result.memberInfo == null) {
274 MethodInfo method = result.classInfo.findMethod(mem, params, paramDimensions);
275 if (method != null) {
276 result.classInfo = method.containingClass();
277 result.memberInfo = method;
282 result.referencedMemberName = mem;
283 if (params != null) {
284 result.referencedMemberName = result.referencedMemberName + '(';
288 for (int i=0; i<len; i++) {
289 result.referencedMemberName = result.referencedMemberName + params[i]
290 + paramDimensions[i] + ", ";
292 result.referencedMemberName = result.referencedMemberName + params[len]
293 + paramDimensions[len];
295 result.referencedMemberName = result.referencedMemberName + ")";
300 result.label = result.label + "/" + ref + "/" + mem + '/';
301 if (params != null) {
302 for (int i=0; i<params.length; i++) {
303 result.label += params[i] + "|";
307 FieldInfo f = (result.memberInfo instanceof FieldInfo)
308 ? (FieldInfo)result.memberInfo
310 MethodInfo m = (result.memberInfo instanceof MethodInfo)
311 ? (MethodInfo)result.memberInfo
313 result.label = result.label
314 + "/package=" + (result.packageInfo!=null?result.packageInfo.name():"")
315 + "/class=" + (result.classInfo!=null?result.classInfo.qualifiedName():"")
316 + "/field=" + (f!=null?f.name():"")
317 + "/method=" + (m!=null?m.name():"");
321 MethodInfo method = null;
322 boolean skipHref = false;
324 if (result.memberInfo != null && result.memberInfo.isExecutable()) {
325 method = (MethodInfo)result.memberInfo;
328 if (text.startsWith("\"")) {
329 // literal quoted reference (e.g., a book title)
330 Matcher matcher = QUOTE_PATTERN.matcher(text);
331 if (! matcher.matches()) {
332 Errors.error(Errors.UNRESOLVED_LINK, pos,
333 "unbalanced quoted link/see tag: " + text.trim());
338 result.label = matcher.group(1);
339 result.kind = "@seeJustLabel";
341 else if (text.startsWith("<")) {
342 // explicit "<a href" form
343 Matcher matcher = HREF_PATTERN.matcher(text);
344 if (! matcher.matches()) {
345 Errors.error(Errors.UNRESOLVED_LINK, pos,
346 "invalid <a> link/see tag: " + text.trim());
350 result.href = matcher.group(1);
351 result.label = matcher.group(2);
352 result.kind = "@seeHref";
354 else if (result.packageInfo != null) {
355 result.href = result.packageInfo.htmlPage();
356 if (result.label.length() == 0) {
357 result.href = result.packageInfo.htmlPage();
358 result.label = result.packageInfo.name();
361 else if (result.classInfo != null && result.referencedMemberName == null) {
363 if (result.label.length() == 0) {
364 result.label = result.classInfo.name();
366 result.href = result.classInfo.htmlPage();
368 else if (result.memberInfo != null) {
370 ClassInfo containing = result.memberInfo.containingClass();
371 if (result.memberInfo.isExecutable()) {
372 if (result.referencedMemberName.indexOf('(') < 0) {
373 result.referencedMemberName += method.flatSignature();
376 if (result.label.length() == 0) {
377 result.label = result.referencedMemberName;
379 result.href = containing.htmlPage() + '#' + result.memberInfo.anchor();
382 if (result.href == null && !skipHref) {
383 if (printOnErrors && (base == null || base.checkLevel())) {
384 Errors.error(Errors.UNRESOLVED_LINK, pos,
385 "Unresolved link/see tag \"" + text.trim()
386 + "\" in " + ((base != null) ? base.qualifiedName() : "[null]"));
390 else if (result.memberInfo != null && !result.memberInfo.checkLevel()) {
391 if (printOnErrors && (base == null || base.checkLevel())) {
392 Errors.error(Errors.HIDDEN_LINK, pos,
393 "Link to hidden member: " + text.trim());
396 result.kind = "@seeJustLabel";
398 else if (result.classInfo != null && !result.classInfo.checkLevel()) {
399 if (printOnErrors && (base == null || base.checkLevel())) {
400 Errors.error(Errors.HIDDEN_LINK, pos,
401 "Link to hidden class: " + text.trim() + " label=" + result.label);
404 result.kind = "@seeJustLabel";
406 else if (result.packageInfo != null && !result.packageInfo.checkLevel()) {
407 if (printOnErrors && (base == null || base.checkLevel())) {
408 Errors.error(Errors.HIDDEN_LINK, pos,
409 "Link to hidden package: " + text.trim());
412 result.kind = "@seeJustLabel";
420 public boolean checkLevel() {
421 if (memberInfo != null) {
422 return memberInfo.checkLevel();
424 if (classInfo != null) {
425 return classInfo.checkLevel();
427 if (packageInfo != null) {
428 return packageInfo.checkLevel();
433 /** turn this LinkReference into one with an error message */
434 private void makeError() {
435 //this.href = "ERROR(" + this.text.trim() + ")";
437 if (this.label == null) {
440 this.label = "ERROR(" + this.label + "/" + text.trim() + ")";
444 private LinkReference() {