001: package tide.classsyntax;
002:
003: import tide.editor.MainEditorFrame;
004: import tide.utils.SyntaxUtils;
005: import snow.utils.StringUtils;
006: import snow.utils.storage.FileUtils;
007: import java.lang.reflect.*;
008: import snow.html.HTMLViewer;
009: import javax.swing.*;
010: import java.io.*;
011: import java.util.*;
012:
013: /** Easy small parser to fetch the doc from a JavaDoc HTML page
014: * not really very robust... but works fine.
015: * Tested with 1.5 and 1.6. ok.
016: *
017: * TODO: indexof tags should be case insensitive (?) !! StringUtils
018: */
019: public final class JavaDocParser {
020: private JavaDocParser() {
021: }
022:
023: public static File getJavaDocFile(String className,
024: File javaDocBase, boolean packageSummary) {
025: if (packageSummary) {
026: File path = new File(javaDocBase, className.replace('.',
027: '/')
028: + "/package-summary.html");
029: if (!path.exists()) {
030: return null;
031: }
032:
033: return path;
034: } else {
035: String fileName = className.replace('.', '/') + ".html";
036: // for inner classes
037: fileName = fileName.replace('$', '.'); // ex: java.awt.geom.RoundRectangle2D$Float
038:
039: File path = new File(javaDocBase, fileName);
040: if (!path.exists())
041: return null;
042:
043: return path;
044: }
045: }
046:
047: public static String getHTMLContent(File javaDocFile) {
048: if (!javaDocFile.exists())
049: throw new RuntimeException("JavaDoc file not found at "
050: + javaDocFile);
051:
052: try {
053: return new String(FileUtils
054: .getFileStringContent(javaDocFile));
055: } catch (Exception e) {
056: throw new RuntimeException(e);
057: }
058: }
059:
060: public static String getPackageSummaryFromCompleteContent(
061: String cont_) {
062: String cont = cont_;
063: // remove the bottom bar
064:
065: int posEnd = StringUtils.indexOfIgnoreCases_SLOW(cont,
066: "<A NAME=\"navbar_bottom\"");
067: //int posEnd = cont.indexOf("<A NAME=\"navbar_bottom\""); // 1.6: "<a NAME=..."
068: if (posEnd > 0) {
069: cont = cont.substring(0, posEnd);
070: } else {
071: String bn = "<!-- ======= START OF BOTTOM NAVBAR ====== -->";
072: int bnp = cont.indexOf(bn);
073: if (bnp >= 0)
074: cont = cont.substring(0, bnp);
075: }
076:
077: // remove the top bar !
078: String skipStart = "<A NAME=\"skip-navbar_top\"></A>";
079: int posStart = StringUtils.indexOfIgnoreCases_SLOW(cont,
080: skipStart); // cont.indexOf(skipStart);
081: if (posStart > 0) {
082: cont = cont.substring(posStart + skipStart.length());
083: }
084:
085: cont = removeHTMLComments(cont);
086:
087: cont = compact(cont);
088:
089: int pos = StringUtils.indexOfIgnoreCases_SLOW(cont,
090: "<A NAME=\"package_description\">");
091: if (pos < 0) {
092: //return "Not found";
093: return trimHTML(cont);
094: }
095: cont = cont.substring(pos + 30);
096:
097: // remove the header ("<h2>Package xxx Description</h2>")
098: pos = cont.indexOf("</H2>");
099: if (pos > 0) {
100: cont = cont.substring(pos + 5);
101: }
102:
103: return trimHTML(cont);
104: }
105:
106: /** recursively eliminate starting and ending HR, BR and P tags
107: */
108: @tide.annotations.Recurse
109: public static String trimHTML(String cont) {
110: cont = cont.trim();
111: if (cont.length() == 0)
112: return cont;
113: if (cont.startsWith("<P>")) {
114: return trimHTML(cont.substring(3));
115: }
116: if (cont.startsWith("<BR>")) {
117: return trimHTML(cont.substring(4));
118: }
119: if (cont.startsWith("<HR>")) {
120: return trimHTML(cont.substring(4));
121: }
122:
123: if (cont.endsWith("<HR>")) {
124: return trimHTML(cont.substring(0, cont.length() - 4));
125: }
126: if (cont.endsWith("<BR>")) {
127: return trimHTML(cont.substring(0, cont.length() - 4));
128: }
129: if (cont.endsWith("<P>")) {
130: return trimHTML(cont.substring(0, cont.length() - 3));
131: }
132:
133: return cont;
134: }
135:
136: /** class description
137: */
138: public static String getJavaDocClassDescription(String htmlCode_,
139: boolean withoutHead) {
140: // remove the end
141: String htmlCode = htmlCode_;
142: int posEnd = htmlCode.indexOf("<A NAME=\"field_summary\"");
143: if (posEnd > 0) {
144: htmlCode = htmlCode.substring(0, posEnd);
145: }
146: posEnd = htmlCode.indexOf("<A NAME=\"method_summary\"");
147: if (posEnd > 0) {
148: htmlCode = htmlCode.substring(0, posEnd);
149: }
150: posEnd = htmlCode.indexOf("<A NAME=\"constructor_summary\"");
151: if (posEnd > 0) {
152: htmlCode = htmlCode.substring(0, posEnd);
153: }
154: posEnd = htmlCode.indexOf("<A NAME=\"enum_constant_summary\">");
155: if (posEnd > 0) {
156: htmlCode = htmlCode.substring(0, posEnd);
157: }
158:
159: // skip the top
160: String lookFor = "<!-- ======== START OF CLASS DATA ======== -->";
161: int pf = htmlCode.indexOf(lookFor);
162: if (pf >= 0) {
163: htmlCode = htmlCode.substring(pf + lookFor.length());
164: } else {
165: // no more in 1.6...
166: String topSkip = "<A NAME=\"skip-navbar_top\"></A>";
167: int posTop = StringUtils.indexOfIgnoreCases_SLOW(htmlCode,
168: topSkip);
169: if (posTop > 0) {
170: htmlCode = htmlCode
171: .substring(posTop + topSkip.length());
172: }
173: }
174:
175: String endComm = "<!-- ========= END OF CLASS DATA ========= -->";
176: posEnd = htmlCode.indexOf(endComm);
177: if (posEnd >= 0) {
178: htmlCode = htmlCode.substring(0, posEnd);
179: }
180:
181: /*??? if(withoutHead)
182: {
183: // take after first <BR> // class or interface name (before comes the package name)
184: int pos = htmlCode.indexOf("<BR>");
185: if(pos>0)
186: {
187: htmlCode = htmlCode.substring(pos+4).trim();
188: }
189: }*/
190:
191: // beautify the code:
192: htmlCode = removeHTMLComments(htmlCode); // ? can be used to better extract things ??
193: htmlCode = compact(htmlCode);
194: htmlCode = replaceImages(htmlCode);
195:
196: return trimHTML(htmlCode);
197: }
198:
199: /** class description
200: *
201: public static String getJavaDocConstructor(String signature, String htmlCode)
202: {
203: return getJavaDocMethodDetail(signature, htmlCode);
204: }*/
205:
206: /** regex this !!
207: */
208: public static String replaceImages(String cont) {
209: // regex syntax !
210: cont = cont
211: .replaceAll(
212: "\\Q<IMG SRC=\"\\E(../)*\\Qresources/inherit.gif\" ALT=\"extended by \">\\E",
213: "+ ");
214: // <IMG SRC="../../resources/inherit.gif" ALT="extended by ">
215:
216: return cont;
217: }
218:
219: /** @param name of the constructor (class simple name) or method or field
220: For fields, the arguments must be null (this permit to distinguish between a.hello and a.hello()
221:
222: @return the position of the <a name="+name position, -1 if not found
223: the javadoc section starts after the next </a> following.
224: Better use the next </h3> to skip all the names
225:
226: */
227: public static int parse_MethodFieldOrConstructor_JavaDoc(
228: String name, Class[] arguments, Type[] types,
229: boolean isVarArgs, String htmlCode) {
230: // iterate over the tags <a name= + name
231: int actualNamePos = -1;
232: String startToSearch = "<A NAME=\"" + name;
233: boolean field = arguments == null;
234:
235: if (!field)
236: startToSearch += "(";
237:
238: if (field)
239: startToSearch += "\""; // ends
240:
241: //System.out.println("Searching in doc for "+startToSearch);
242:
243: int iter = 0;
244: while (true) {
245: iter++;
246: if (iter > 1000) {
247: MainEditorFrame
248: .debugOut("TOO much searches in parse_MethodFieldOrConstructor_JavaDoc");
249: break;
250: }
251:
252: actualNamePos = htmlCode.indexOf(startToSearch,
253: actualNamePos + 1);
254: if (actualNamePos == -1)
255: return -1; // not found
256:
257: if (field) {
258: // we found it.
259: break;
260: }
261:
262: // try to match the arguments
263: actualNamePos += startToSearch.length();
264: if (hasSignatureAt(arguments, types, isVarArgs, htmlCode,
265: actualNamePos, true)) {
266: // yeah, it matches, look if it also has a closing parenthesis )
267: break;
268: }
269: // no it has NOT => go to the next
270: }
271: // we found it !
272: return actualNamePos;
273: }
274:
275: /** @return true if the given signature is present at the given position
276: */
277: public static boolean hasSignatureAt(Class[] arguments,
278: Type[] types, boolean isVarArgs, String htmlCode, int pos,
279: boolean andClosingParenthesis) {
280: // ex: "../../java/lang/Class.html#getDeclaredMethod(java.lang.String, java.lang.Class...)
281:
282: int actualArgStart = pos;
283:
284: for (int i = 0; i < arguments.length; i++) {
285: String argi = "" + types[i]; // java.util.Collection<? extends E>
286:
287: // [march2007] in the constr of ProcessBuilder: "class [Ljava.lang.String;"
288: if (argi.startsWith("class ")) {
289: argi = argi.substring(6);
290: }
291:
292: // [March2007]: remove generics ( tested on Vector and Class => total success !!)
293: argi = SyntaxUtils.removeSingleTypeParameters(argi);
294:
295: // ProcessBuilder constructor (for example)
296: argi = SyntaxUtils.convertVmNameToFullJavaNames(argi);
297:
298: // varargs are only for the last position
299: if (isVarArgs && i == arguments.length - 1) {
300: argi = argi.substring(0, argi.length() - 2) + "...";
301: }
302: //System.out.println("Search arg \""+argi+"\"");
303: // skip blanks
304: actualArgStart = skip(htmlCode, actualArgStart, " \r\n\t");
305: if (actualArgStart == -1) {
306: MainEditorFrame
307: .debugOut("hasSignatureAt stop 1 before "
308: + argi);
309: return false;
310: }
311:
312: if (!htmlCode.regionMatches(actualArgStart, argi, 0, argi
313: .length())) {
314: // look at "another" signature
315: // Search alternate arg 2 java.lang.Thread.UncaughtExceptionHandler instead of interface java.lang.Thread$UncaughtExceptionHandler
316:
317: //System.out.println("Search alternate arg 2 "+arguments[i].getCanonicalName()+" instead of "+argi);
318: argi = arguments[i].getCanonicalName();
319:
320: if (!htmlCode.regionMatches(actualArgStart, argi, 0,
321: argi.length())) {
322: return false;
323: }
324:
325: }
326: // yeah, the region matches => avance
327: actualArgStart += argi.length();
328:
329: // skip blanks and ,
330: actualArgStart = skip(htmlCode, actualArgStart, " ,\r\n\t");
331: if (actualArgStart == -1) {
332: MainEditorFrame.debugOut("hasSignatureAt stop 2 after "
333: + argi);
334: return false;
335: }
336: }
337:
338: if (andClosingParenthesis) {
339: if (htmlCode.charAt(actualArgStart) != ')') {
340: //System.out.println("No ending ) found");
341: return false;
342: }
343: }
344:
345: // all args matched
346: return true;
347: }
348:
349: /** -1 if end reached.
350: */
351: public static int skip(String source, int pos, String charsToSkip) {
352: for (int i = pos; i < source.length(); i++) {
353: if (charsToSkip.indexOf(source.charAt(i)) == -1)
354: return i;
355: }
356: return -1;
357: }
358:
359: /** From the first < /H3> up to the first <HR> of <!-- ===
360: */
361: public static String extractDescrAfter(int pos, String htmlCode) {
362: // skip the title
363: int posH3 = htmlCode.indexOf("</H3>", pos);
364: if (posH3 < 0)
365: posH3 = pos;
366: int end = htmlCode.indexOf("<HR>", posH3);
367: if (end < 0)
368: return trimHTML(htmlCode);
369:
370: // important when scanning the last constructor.
371: int posNext = htmlCode.indexOf("<!-- ======", posH3);
372: if (posNext > 0 && posNext < end)
373: end = posNext;
374:
375: return trimHTML(htmlCode.substring(posH3, end));
376: }
377:
378: private static void viewHTML(String code) {
379: JFrame f = new JFrame("Html");
380: JTextPane edp = new JTextPane();
381: edp.setEditable(false);
382: edp.setContentType("text/html");
383: edp.setText(code);
384: f.add(new JScrollPane(edp));
385: //f.pack();
386: f.setSize(400, 400);
387: f.setLocationRelativeTo(null); // screen
388: f.setVisible(true);
389: }
390:
391: /** Clean the code, removing comments.
392: */
393: public static String removeHTMLComments(String src) {
394: String ret = src;
395: int posStart = -1;
396: while ((posStart = ret.indexOf("<!--")) >= 0) {
397: int posEnd = ret.indexOf("-->", posStart + 4);
398: if (posEnd == -1)
399: return ret;
400:
401: ret = ret.substring(0, posStart)
402: + ret.substring(posEnd + 3);
403: }
404: return ret;
405: }
406:
407: /** Removes multiples <P>.
408: */
409: public static String compact(String src) {
410: String ret = src;
411: ret = ret.replaceAll("(<P>\\s*)+", "<P>"); // regex !
412: return ret;
413: }
414:
415: public static String getJavaDocDescription(Method met,
416: String htmlContent) {
417: if (met == null) {
418: MainEditorFrame.debugOut("JDP: null met");
419: return "<html>Cannot find javadoc of null method";
420: }
421:
422: if (htmlContent == null) {
423: MainEditorFrame.debugOut("JDP: null htmlContent");
424: return "<html>Cannot find javadoc of method " + met
425: + ": null html";
426: }
427:
428: int pos = parse_MethodFieldOrConstructor_JavaDoc(met.getName(),
429: met.getParameterTypes(),
430: met.getGenericParameterTypes(), met.isVarArgs(),
431: htmlContent);
432: if (pos > 0) {
433: return trimHTML(compact(extractDescrAfter(pos, htmlContent)));
434: } else {
435: return "<html>Cannot find javadoc of method " + met;
436: }
437: }
438:
439: public static String getJavaDocDescription(Constructor met,
440: String htmlContent) {
441: // Java BUG, in the Constructor documentation, it is stated that getName() is the simple name.
442: String simpleName = met.getDeclaringClass().getSimpleName();
443:
444: //System.out.println("Constructor: "+met+" "+Arrays.toString(met.getGenericParameterTypes()));
445: //System.out.println("Declared: "+met.getDeclaringClass());
446: int pos = parse_MethodFieldOrConstructor_JavaDoc(simpleName,
447: met.getParameterTypes(),
448: met.getGenericParameterTypes(), met.isVarArgs(),
449: htmlContent);
450: if (pos > 0) {
451: return trimHTML(compact(extractDescrAfter(pos, htmlContent)));
452: } else {
453: //System.out.println("################## HTMLConstr="+htmlContent);
454: return "<html>Cannot find javadoc of constructor " + met;
455: }
456: }
457:
458: public static String getJavaDocDescription(Field f,
459: String htmlContent) {
460: int pos = parse_MethodFieldOrConstructor_JavaDoc(f.getName(),
461: null, new Type[0], false, htmlContent);
462: if (pos > 0) {
463: return trimHTML(compact(extractDescrAfter(pos, htmlContent)));
464: } else {
465: return "<html>Cannot find javadoc of field " + f;
466: }
467:
468: }
469:
470: /*
471: public static List<String> extractArgumentNames(String cont)
472: {
473: List<String>an = new ArrayList<String>();
474: return an;
475: }*/
476:
477: public static void main(String[] a) {
478: Class c = String.class;
479: Constructor co = c.getConstructors()[0];
480: // Returns the name of this constructor, as a string. This is always the same as the simple name of the constructor's declaring class.
481:
482: // Constructor decl class simple name
483: assert co.getDeclaringClass().getSimpleName().equals("String");
484: // Constructor name
485: assert co.getName().equals("String");
486:
487: }
488:
489: public static void main2(String[] a) {
490: //System.out.println(""+removeHTMLComments("123<-- as -->456<-- s-->."));
491: //System.out.println(compact("<P><P> <P> aa <P> <P> bb <P> c <P> <P>."));
492: //System.out.println(""+ replaceImages("<IMG SRC=\"../../resources/inherit.gif\" ALT=\"extended by \">"));
493: File base = new File("C:/Java/docs/jdk6.0docs/api");
494: File file = getJavaDocFile("java.lang.String", base, false);
495: String cont = getHTMLContent(file);
496:
497: //String cont = getHTMLContent("javax.swing.border.TitledBorder", base);
498: //String cont = getHTMLContent("java.lang.instrument.UnmodifiableClassException", base);
499: //String cont = getHTMLContent("java.util.Vector", base);
500: //String doc = getJavaDocMethodDetail("paintBorder", cont);
501: //String doc = getJavaDocMethodSummary("paintBorder", cont);
502: //String doc = getJavaDoc_fieldOrMethod("BELOW_TOP", cont);
503: //String doc = getJavaDocClassDescription(cont, false);
504: //String doc = getJavaDoc_fieldOrMethod("add(int,java.lang.Object)", cont);
505: //String doc = getJavaDoc_fieldOrMethod("removeElementAt(int)", cont);
506:
507: //String doc = getPackageSummary( getPackageSummaryHTML("javax.swing", base));
508: //doc = replaceImages(doc);
509: //System.out.println(""+doc);
510: //viewHTML(doc);
511: Class c = String.class;
512: Constructor co = c.getConstructors()[0];
513: int pos = parse_MethodFieldOrConstructor_JavaDoc(co
514: .getDeclaringClass().getSimpleName(), co
515: .getParameterTypes(), co.getGenericParameterTypes(), co
516: .isVarArgs(), cont);
517:
518: //int pos = parse_MethodFieldOrConstructor_JavaDoc("CASE_INSENSITIVE_ORDER", null, new Type[0], cont);
519:
520: //int pos = parse_MethodFieldOrConstructor_JavaDoc("CASE_INSENSITIVE_ORDER", null, new Type[0], cont);
521: if (pos > 0) {
522: cont = extractDescrAfter(pos, cont);
523: //System.out.println(""+cont);
524: } else {
525: System.out.println("<html>NOT FOUND");
526: }
527:
528: JFrame fr = new JFrame();
529: fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
530: HTMLViewer vi = new HTMLViewer();
531: fr.add(vi);
532: fr.setSize(400, 600);
533: vi.setHTMLContent(cont);
534: fr.setVisible(true);
535:
536: System.out.println("ok");
537: }
538:
539: }
|