OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / build / tools / droiddoc / src / LinkReference.java
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 import java.util.regex.Pattern;
18 import java.util.regex.Matcher;
19 import java.util.ArrayList;
20
21 /**
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).
24  */
25 public class LinkReference {
26
27     /** The original text. */
28     public String text;
29     
30     /** The kind of this tag, if we have a new suggestion after parsing. */
31     public String kind;
32
33     /** The user visible text. */
34     public String label;
35
36     /** The link. */
37     public String href;
38
39     /** The {@link PackageInfo} if any. */
40     public PackageInfo packageInfo;
41
42     /** The {@link ClassInfo} if any. */
43     public ClassInfo classInfo;
44
45     /** The {@link MemberInfo} if any. */
46     public MemberInfo memberInfo;
47
48     /** The name of the referenced member PackageInfo} if any. */
49     public String referencedMemberName;
50
51     /** Set to true if everything is a-ok */
52     public boolean good;
53
54     /**
55      * regex pattern to use when matching explicit "<a href" reference text
56      */
57     private static final Pattern HREF_PATTERN
58             = Pattern.compile("^<a href=\"([^\"]*)\">([^<]*)</a>[ \n\r\t]*$",
59                               Pattern.CASE_INSENSITIVE);
60
61     /**
62      * regex pattern to use when matching double-quoted reference text
63      */
64     private static final Pattern QUOTE_PATTERN
65             = Pattern.compile("^\"([^\"]*)\"[ \n\r\t]*$");
66
67     /**
68      * Parse and resolve a link string.
69      *
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.
75      */
76     public static LinkReference parse(String text, ContainerInfo base, SourcePositionInfo pos,
77                                         boolean printOnErrors) {
78         LinkReference result = new LinkReference();
79         result.text = text;
80
81         int index;
82         int len = text.length();
83         int pairs = 0;
84         int pound = -1;
85         // split the string
86         done: {
87             for (index=0; index<len; index++) {
88                 char c = text.charAt(index);
89                 switch (c)
90                 {
91                     case '(':
92                         pairs++;
93                         break;
94                     case '[':
95                         pairs++;
96                         break;
97                     case ')':
98                         pairs--;
99                         break;
100                     case ']':
101                         pairs--;
102                         break;
103                     case ' ':
104                     case '\t':
105                     case '\r':
106                     case '\n':
107                         if (pairs == 0) {
108                             break done;
109                         }
110                         break;
111                     case '#':
112                         if (pound < 0) {
113                             pound = index;
114                         }
115                         break;
116                 }
117             }
118         }
119         if (index == len && pairs != 0) {
120             Errors.error(Errors.UNRESOLVED_LINK, pos,
121                         "unable to parse link/see tag: " + text.trim());
122             return result;
123         }
124
125         int linkend = index;
126
127         for (; index<len; index++) {
128             char c = text.charAt(index);
129             if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
130                 break;
131             }
132         }
133
134         result.label = text.substring(index);
135
136         String ref;
137         String mem;
138         if (pound == 0) {
139             ref = null;
140             mem = text.substring(1, linkend);
141         }
142         else if (pound > 0) {
143             ref = text.substring(0, pound);
144             mem = text.substring(pound+1, linkend);
145         }
146         else {
147             ref = text.substring(0, linkend);
148             mem = null;
149         }
150
151         // parse parameters, if any
152         String[] params = null;
153         String[] paramDimensions = null;
154         if (mem != null) {
155             index = mem.indexOf('(');
156             if (index > 0) {
157                 ArrayList<String> paramList = new ArrayList<String>();
158                 ArrayList<String> paramDimensionList = new ArrayList<String>();
159                 len = mem.length();
160                 int start = index+1;
161                 final int START = 0;
162                 final int TYPE = 1;
163                 final int NAME = 2;
164                 int dimension = 0;
165                 int arraypair = 0;
166                 int state = START;
167                 int typestart = 0;
168                 int typeend = -1;
169                 for (int i=start; i<len; i++) {
170                     char c = mem.charAt(i);
171                     switch (state)
172                     {
173                         case START:
174                             if (c!=' ' && c!='\t' && c!='\r' && c!='\n') {
175                                 state = TYPE;
176                                 typestart = i;
177                             }
178                             break;
179                         case TYPE:
180                             if (c == '[') {
181                                 if (typeend < 0) {
182                                     typeend = i;
183                                 }
184                                 dimension++;
185                                 arraypair++;
186                             }
187                             else if (c == ']') {
188                                 arraypair--;
189                             }
190                             else if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
191                                 if (typeend < 0) {
192                                     typeend = i;
193                                 }
194                             }
195                             else {
196                                 if (typeend >= 0 || c == ')' || c == ',') {
197                                     if (typeend < 0) {
198                                         typeend = i;
199                                     }
200                                     String s = mem.substring(typestart, typeend);
201                                     paramList.add(s);
202                                     s = "";
203                                     for (int j=0; j<dimension; j++) {
204                                         s += "[]";
205                                     }
206                                     paramDimensionList.add(s);
207                                     state = START;
208                                     typeend = -1;
209                                     dimension = 0;
210                                     if (c == ',' || c == ')') {
211                                         state = START;
212                                     } else {
213                                         state = NAME;
214                                     }
215                                 }
216                             }
217                             break;
218                         case NAME:
219                             if (c == ',' || c == ')') {
220                                 state = START;
221                             }
222                             break;
223                     }
224
225                 }
226                 params = paramList.toArray(new String[paramList.size()]);
227                 paramDimensions = paramDimensionList.toArray(new String[paramList.size()]);
228                 mem = mem.substring(0, index);
229             }
230         }
231
232         ClassInfo cl = null;
233         if (base instanceof ClassInfo) {
234             cl = (ClassInfo)base;
235         }
236
237         if (ref == null) {
238             // no class or package was provided, assume it's this class
239             if (cl != null) {
240                 result.classInfo = cl;
241             }
242         } else {
243             // they provided something, maybe it's a class or a package
244             if (cl != null) {
245                 result.classInfo = cl.extendedFindClass(ref);
246                 if (result.classInfo == null) {
247                     result.classInfo = cl.findClass(ref);
248                 }
249                 if (result.classInfo == null) {
250                     result.classInfo = cl.findInnerClass(ref);
251                 }
252             }
253             if (result.classInfo == null) {
254                 result.classInfo = Converter.obtainClass(ref);
255             }
256             if (result.classInfo == null) {
257                 result.packageInfo = Converter.obtainPackage(ref);
258             }
259         }
260
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
267                 // typed.
268                 if (field != null) {
269                     result.classInfo = field.containingClass();
270                     result.memberInfo = field;
271                 }
272             }
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;
278                 }
279             }
280         }
281
282         result.referencedMemberName = mem;
283         if (params != null) {
284             result.referencedMemberName = result.referencedMemberName + '(';
285             len = params.length;
286             if (len > 0) {
287                 len--;
288                 for (int i=0; i<len; i++) {
289                     result.referencedMemberName = result.referencedMemberName + params[i]
290                             + paramDimensions[i] + ", ";
291                 }
292                 result.referencedMemberName = result.referencedMemberName + params[len]
293                         + paramDimensions[len];
294             }
295             result.referencedMemberName = result.referencedMemberName + ")";
296         }
297
298         // debugging spew
299         if (false) {
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] + "|";
304                 }
305             }
306
307             FieldInfo f = (result.memberInfo instanceof FieldInfo)
308                         ? (FieldInfo)result.memberInfo
309                         : null;
310             MethodInfo m = (result.memberInfo instanceof MethodInfo)
311                         ? (MethodInfo)result.memberInfo
312                         : null;
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():"");
318             
319         }
320
321         MethodInfo method = null;
322         boolean skipHref = false;
323
324         if (result.memberInfo != null && result.memberInfo.isExecutable()) {
325            method = (MethodInfo)result.memberInfo;
326         }
327
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());
334                 result.makeError();
335                 return result;
336             }
337             skipHref = true;
338             result.label = matcher.group(1);
339             result.kind = "@seeJustLabel";
340         }
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());
347                 result.makeError();
348                 return result;
349             }
350             result.href = matcher.group(1);
351             result.label = matcher.group(2);
352             result.kind = "@seeHref";
353         }
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();
359             }
360         }
361         else if (result.classInfo != null && result.referencedMemberName == null) {
362             // class reference
363             if (result.label.length() == 0) {
364                 result.label = result.classInfo.name();
365             }
366             result.href = result.classInfo.htmlPage();
367         }
368         else if (result.memberInfo != null) {
369             // member reference
370             ClassInfo containing = result.memberInfo.containingClass();
371             if (result.memberInfo.isExecutable()) {
372                 if (result.referencedMemberName.indexOf('(') < 0) {
373                     result.referencedMemberName += method.flatSignature();
374                 }
375             } 
376             if (result.label.length() == 0) {
377                 result.label = result.referencedMemberName;
378             }
379             result.href = containing.htmlPage() + '#' + result.memberInfo.anchor();
380         }
381
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]"));
387             }
388             result.makeError();
389         }
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());
394                 result.href = null;
395             }
396             result.kind = "@seeJustLabel";
397         }
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);
402                 result.href = null;
403             }
404             result.kind = "@seeJustLabel";
405         }
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());
410                 result.href = null;
411             }
412             result.kind = "@seeJustLabel";
413         }
414
415         result.good = true;
416
417         return result;
418     }
419
420     public boolean checkLevel() {
421         if (memberInfo != null) {
422             return memberInfo.checkLevel();
423         }
424         if (classInfo != null) {
425             return classInfo.checkLevel();
426         }
427         if (packageInfo != null) {
428             return packageInfo.checkLevel();
429         }
430         return false;
431     }
432
433     /** turn this LinkReference into one with an error message */
434     private void makeError() {
435         //this.href = "ERROR(" + this.text.trim() + ")";
436         this.href = null;
437         if (this.label == null) {
438             this.label = "";
439         }
440         this.label = "ERROR(" + this.label + "/" + text.trim() + ")";
441     }
442
443     /** private. **/
444     private LinkReference() {
445     }
446 }