// JavaParser.java, by Rowland // Class to parse a java source file. // Inspired by a sample from www.codeguru.com // Compatible with JDK 1.3 import java.io.*; import java.util.*; class SourceTokenizer { protected StreamTokenizer st; protected int ttype_prev= StreamTokenizer.TT_EOL; // Callbacks. Override these in your derived class for useful functionality... void callbackToken(int ttype, double nval, String sval) { } private final void combine2chars() { // helper for givenFileInputStream() char data[]= {(char)ttype_prev, (char)st.ttype}; st.ttype= StreamTokenizer.TT_WORD; callbackToken(st.ttype, 0, new String(data)); } private final void separate2chars() { // helper for givenFileInputStream() callbackToken(ttype_prev, 0, ""); callbackToken(st.ttype, st.nval, st.sval); } void givenFileInputStream(FileInputStream file) throws IOException { System.gc(); Reader r = new BufferedReader(new InputStreamReader(file)); st = new StreamTokenizer(r); st.eolIsSignificant(true); st.quoteChar('"'); st.quoteChar('\''); st.slashSlashComments(true); st.slashStarComments(true); st.ordinaryChar('.'); st.ordinaryChar('-'); st.wordChars('_', '_'); ttype_prev= StreamTokenizer.TT_EOL; while (st.nextToken()!= StreamTokenizer.TT_EOF) { // Some special parsing for two-character operators: switch (ttype_prev) { case '=': case '!': if (st.ttype== '=') combine2chars(); else separate2chars(); break; case '>': case '<': case '+': case '-': if (st.ttype== ttype_prev) combine2chars(); else if (st.ttype== '=') combine2chars(); else separate2chars(); break; case '*': case '/': // not comments. we deal with those via config of st if (st.ttype== '=') combine2chars(); else separate2chars(); break; default: switch (st.ttype) { case '=': case '!': case '>': case '<': case '/': case '*': case '+': case '-': break; // hold off until we know what it is default: callbackToken(st.ttype, st.nval, st.sval); break; } break; } if (st.ttype!= StreamTokenizer.TT_EOL) // want to ignore line breaks ttype_prev= st.ttype; } System.gc(); } void givenFilename(String filename) throws IOException { givenFileInputStream(new FileInputStream(filename)); } } //-------------------------------------------------------------- /** Factbase: a collection of facts we have inferred about the source code we've parsed **/ class Factbase { protected Hashtable hSubjects= new Hashtable(); static Map getVerbTable(Map hSubject, String kVerb) { // A table of objects for a given subject and verb. Creates the verb table if need be Object O= hSubject.get(kVerb); if (O== null) { O= new TreeMap(); hSubject.put(kVerb, O); } return (Map)O; } final Map getVerbTable(String subjName, String kVerb) { if (subjName== null) System.err.println("getVerbTable(null)"); Map hSubject= (Map)getSubject(subjName); return getVerbTable(hSubject, kVerb); } final String getVerbFirst(String subjName, String kVerb) { Map hSubject= (Map)getSubject(subjName); if (hSubject== null) return null; Map O= (Map)hSubject.get(kVerb); if (O== null) return null; Set S= O.keySet(); if (S==null) return null; Iterator I= S.iterator(); if (I== null) return null; return (String)I.next(); } final boolean hasVerb(String subjName, String kVerb) { if (subjName== null) return false; if (kVerb== null) return false; Map hSubject= (Map)getSubject(subjName); if (hSubject== null) return false; Object O= hSubject.get(kVerb); return O!= null; } static protected void put(Map hSubject, String kVerb, String val) { if (val== null) return; Map hVerb= getVerbTable(hSubject, kVerb); hVerb.put(val, ""); // never pass null for a value. it will choke. // ...in the future, we may use the value as an abverb of some sort. //if (val.equals("i")) System.exit(1); } final void put(String subjName, String kVerb, String val) { if (subjName== null) return; Map hSubject= getSubject(subjName); put(hSubject, kVerb, val); // Now for the reverse chain... if (val== null) System.err.println("put("+ subjName+ ", "+ kVerb+ ", null)"); Map hVal= getSubject(val); put(hVal, "#"+ kVerb, subjName); } /** use this when the subject is path with dot separators, to note partially-qualified subpaths **/ final void putAmbigSubjectIsa(String subjName, String isa) { if (query(subjName, "ISA", isa)) return; // already done put(subjName, "ISA", isa); int lastdot= subjName.lastIndexOf('.'); if (lastdot<= 0) return; // degenerate cases String right= subjName.substring(lastdot+1); String left= subjName.substring(0, lastdot); put(right, "ISA", isa); put(right, "MAYREFERTO", subjName); put(right, "CHILDOF", left); if (query(left, "ISA", isa)) return; // already done put(left, "ISA", isa); if (lastdot== subjName.indexOf('.')) return; // should draw the line somewhere, i suppose putAmbigSubjectIsa(left, isa); // recurse to cover more cases } /** use this when the subject is parent.subparent.subparent.child, different isa type for child **/ final void putAmbigSubjectChild(String subjName, String isa) { put(subjName, "ISA", isa); // first things first int lastdot= subjName.lastIndexOf('.'); if (lastdot<= 0) return; // degenerate cases String right= subjName.substring(lastdot+1); String left= subjName.substring(0, lastdot); put(right, "MAYREFERTO", subjName); put(right, "CHILDOF", left); while (true) { // go through possible variations lastdot= left.lastIndexOf('.'); if (lastdot<=0) break; right= left.substring(lastdot+1)+ "."+ right; left= left.substring(0, lastdot); put(right, "MAYREFERTO", subjName); } } final Map getSubject(String subjName) { if (subjName== null) System.err.println("getSubject(null)"); Map retval= (Map)hSubjects.get(subjName); if (retval!= null) return retval; retval= new TreeMap(); hSubjects.put(subjName, retval); // never pass null for a value. return retval; } final boolean hasSubject(String subjName) { if (subjName== null) return false; return hSubjects.get(subjName)!= null; } final boolean query(String subjName, String kVerb, String val) { if (hSubjects== null) return false; if (subjName== null) return false; Map hSubject= (Map)hSubjects.get(subjName); if (hSubject== null) return false; Map hVerb= (Map)hSubject.get(kVerb); if (hVerb== null) return false; return hVerb.containsKey(val); } public Enumeration enumSubjects() { return hSubjects.keys(); } void listResults() { System.out.println(""); Enumeration eSubject= enumSubjects(); while (eSubject.hasMoreElements()) { String kSubject= (String)eSubject.nextElement(); System.out.print("SUBJECT: "); System.out.println(kSubject); listSubject(kSubject); } } void listSubject(String kSubject) { // A subject has verbs and objects associated with it Object O= hSubjects.get(kSubject); if (O== null) return; if (!(O instanceof Map)) return; Map hVerbs= (Map)O; Set sVerbs= hVerbs.keySet(); Iterator iVerbs= sVerbs.iterator(); while (iVerbs.hasNext()) { String kVerb= (String)iVerbs.next(); if (kVerb.startsWith("#")) { // reverse chain O= hVerbs.get(kVerb); if (O== null) break; if (!(O instanceof Map)) break; Map hVerb= (Map)O; Set sObjects= hVerb.keySet(); Iterator iObjects= sObjects.iterator(); while (iObjects.hasNext()) { String kObject= (String)iObjects.next(); System.out.print(" "); System.out.print(kObject); System.out.print(" "); System.out.println(kVerb.substring(1)); } } else { // forward chain O= hVerbs.get(kVerb); if (O== null) break; if (!(O instanceof Map)) break; System.out.print(" "); System.out.print(kVerb); System.out.println("..."); Map hVerb= (Map)O; Set sObjects= hVerb.keySet(); Iterator iObjects= sObjects.iterator(); while (iObjects.hasNext()) { String kObject= (String)iObjects.next(); System.out.print(" "); System.out.println(kObject); } } } } } //-------------------------------------------------------------- /** parse the given Factbase, build an HTML site from its contents **/ class FactbaseToHtml { Factbase FB; String title; List ofInterest; TreeMap groups= new TreeMap(); // we group by prefix, each group gets its own HTML page TreeMap index= new TreeMap(); FactbaseToHtml(Factbase _fb, String _title, List _ofInterest ) { FB= _fb; title= _title; ofInterest= _ofInterest; split(); } /** here we do all the work **/ protected void split() { try { File f= new File("html"); if (!f.isDirectory()) { f.mkdir(); } } catch (Exception E) { System.err.println("Can't create HTML directory: "+ E.getMessage()); E.printStackTrace(); } try { Enumeration eSubjects= FB.enumSubjects(); while (eSubjects.hasMoreElements()) { String sSubject= (String)eSubjects.nextElement(); subjectPut(sSubject); } Set sGroups= groups.keySet(); Iterator iGroups= sGroups.iterator(); while (iGroups.hasNext()) { String ix= (String)iGroups.next(); group(ix); } } catch (Exception E) { System.err.println("Can't convert to HTML: "+ E.getMessage()); E.printStackTrace(); } try { doIndex(); } catch (Exception E) { System.err.println("Can't create index: "+ E.getMessage()); E.printStackTrace(); } } // Decide which group a symbol should go into by taking a useful prefix protected static String prefix(String symbol) { if (symbol.length()< 3) return symbol.toLowerCase(); // be wary of case-insensitive file systems String retval; int lastdot= symbol.lastIndexOf('.'); int lastslash= symbol.lastIndexOf('/'); int lastslash2= symbol.lastIndexOf('\\'); if (lastslash2>= 0) lastslash= lastslash2; if (lastslash< 0) lastslash= lastdot; // preference to slash over dot if (lastslash>= 0) retval= symbol.substring(0, lastslash); else retval= symbol.substring(0, 3); return htmlName(retval).toLowerCase(); // be wary of case-insensitive file systems } // Make a String safe for using as an HTML symbol or filename protected static String htmlName(String symbol) { byte bytes[]= symbol.getBytes(); for (int i=0; i"+ title+ " "+ ix+ "*"); pw.println("

"+ title+ " "+ ix+ "*

"); if (ofInterest!= null) { // a sidebar for subjects of interest pw.println(""); sidebar(pw); pw.println("
"); } groupBody(ix, pw); if (ofInterest!= null) pw.println("
"); pw.println(""); pw.close(); } protected void sidebar(PrintWriter pw) { pw.println(""); Iterator i= ofInterest.iterator(); pw.println("
Main index"); while (i.hasNext()) { String kObject= (String)i.next(); if (FB.hasSubject(kObject)) { // if any actually exist String hrefObject= (String)index.get(kObject); pw.println("
"+ kObject+ ""); } } pw.println(""); } protected void groupBody(String ix, PrintWriter pw) throws Exception { Map h= (Map)groups.get(ix); Set s= h.keySet(); Iterator i= s.iterator(); while (i.hasNext()) { String subject= (String)i.next(); doSubject(subject, pw); } } void doIndex() throws Exception { FileOutputStream fos= new FileOutputStream("html"+ File.separator+ "index.html"); PrintWriter pw = new PrintWriter(fos); pw.println(""+ title+ ""); pw.println("

"+ title+ "

"); if (ofInterest!= null) { // a sidebar for subjects of interest pw.println(""); sidebar(pw); pw.println("
"); } if (index.size()<= 200) doIndexBodyDetailed(pw); else if (index.size()<= 2000) doIndexBodyGroups(pw); else doIndexBodyHuge(pw); if (ofInterest!= null) pw.println("
"); pw.println(""); pw.close(); } void doIndexBodyDetailed(PrintWriter pw) throws Exception { String prevPrefix= ""; Set sKeys= index.keySet(); Iterator ikeys= sKeys.iterator(); while (ikeys.hasNext()) { String subject= (String)ikeys.next(); String hrefSubject= (String)index.get(subject); String prefix= prefix(subject); if (!prefix.equals(prevPrefix)) { pw.println("
"); prevPrefix= prefix; } pw.print(""+ subject+ " "); } } void doIndexBodyGroups(PrintWriter pw) throws Exception { // use this if there are way too many entries for a detailed index to be usable... String prevPrefix= ""; Set sKeys= groups.keySet(); Iterator ikeys= sKeys.iterator(); while (ikeys.hasNext()) { String group= (String)ikeys.next(); String prefix= group.substring(0, 1); if (!prefix.equals(prevPrefix)) { pw.println("
"); prevPrefix= prefix; } pw.print(""+ group+ "* "); } } void doIndexBodyHuge(PrintWriter pw) throws Exception { // Two tier index... // Collate by prefixes... TreeMap byPrefixes= new TreeMap(); Set sKeys= groups.keySet(); Iterator iKeys= sKeys.iterator(); while (iKeys.hasNext()) { String group= (String)iKeys.next(); String prefix= group.substring(0, 1); TreeMap prefixGroups= (TreeMap)byPrefixes.get(prefix); if (prefixGroups== null) { prefixGroups= new TreeMap(); byPrefixes.put(prefix, prefixGroups); } prefixGroups.put(group, ""); } // Write out index pages... sKeys= byPrefixes.keySet(); iKeys=sKeys.iterator(); while (iKeys.hasNext()) { String prefix= (String)iKeys.next(); TreeMap prefixGroups= (TreeMap)byPrefixes.get(prefix); Set sPG= prefixGroups.keySet(); Iterator iPG= sPG.iterator(); FileOutputStream fos= new FileOutputStream("html"+ File.separator+ prefix+ ".index.html"); PrintWriter pwPG = new PrintWriter(fos); pwPG.println(""+ title+ ": "+ prefix+ ""); pwPG.println("

"+ title+ "

"); if (ofInterest!= null) { // a sidebar for subjects of interest pwPG.println(""); sidebar(pwPG); pwPG.println("
"); } while (iPG.hasNext()) { String group= (String)iPG.next(); pwPG.println("
"+ group+ "* "); } if (ofInterest!= null) pwPG.println("
"); pwPG.println(""); pwPG.close(); pw.println("

"+ prefix+ " - subindex

"); } } void doSubject(String kSubject, PrintWriter pw) throws Exception { // A subject has verbs and objects associated with it pw.println("


"); pw.println("

"+ kSubject+ "

"); if (FB.query(kSubject, "ISA", "sourcefile")) { pw.println("
"+ kSubject+ ""); } else { String file= FB.getVerbFirst(kSubject, "INFILE"); if (file!= null) pw.println("
"+ file+ ""); } Map hVerbs= (Map)FB.getSubject(kSubject); Set sVerbs= hVerbs.keySet(); Iterator iVerbs= sVerbs.iterator(); pw.println("
");
	while (iVerbs.hasNext()) {
	    String kVerb= (String)iVerbs.next();
	    if (kVerb.startsWith("#")) { // reverse chain
		Object O= hVerbs.get(kVerb);
		if (O== null)  break;   if (!(O instanceof Map))  break;
		Map hVerb= (Map)O;
		Set sObjects= hVerb.keySet();  Iterator iObjects= sObjects.iterator(); 
		while (iObjects.hasNext()) {
		    String kObject= (String)iObjects.next();
		    String hrefObject= (String)index.get(kObject);
		    pw.print(""+ kObject+ "");
		    pw.print(" ");  
		    pw.println(kVerb.substring(1));
		}
	    }  else { // forward chain
		Object O= hVerbs.get(kVerb);
		if (O== null)  break;   if (!(O instanceof Map))  break;
		Map hVerb= (Map)O;
		Set sObjects= hVerb.keySet();  Iterator iObjects= sObjects.iterator(); 
		while (iObjects.hasNext()) {
		    String kObject= (String)iObjects.next();
		    String hrefObject= (String)index.get(kObject);
		    pw.print(kVerb+ " ");
		    pw.println(""+ kObject+ "");
		}
	    }
	}
	pw.println("
"); } } //-------------------------------------------------------------- class JavaParser extends SourceTokenizer { protected int nesting= 0; // depth of curly bracket nesting. protected boolean sawEquals= false; protected boolean sawDeclaration= false; final int S_NULL= 0; // nothing special state final int S_XPCLASSNAME= 1; // expect class name final int S_XPPARENTCLASSNAME= 2; // expect parent class name final int S_XPPACKAGENAME= 3; // expect package name final int S_XPTHROWSNAME= 4; // expect type of exception thrown final int S_INARGLIST= 5; final int S_XPSECOND= 6; // expect second symbol in statmeent or declaration within method final int S_XPINNERCLASSNAME= 7; // expect class name final int S_SAWDOT= 8; final int S_XPFIELDNAME= 9; protected int state= S_NULL; protected String currentFilename= null; protected String packageName= null; protected String className= null; // Current class protected TreeMap classFields= null; // name->type protected String methodName= null; // Current method protected TreeMap methodFields= null; // name->type protected String innerClassName= null; // Current inner class protected String parentClassName= null; // name of parent class protected String sval= null; protected String sval_prev= null; protected String sval_beforedot= null; protected String sval_first= null; protected Factbase FB= new Factbase(); protected String topName= null; public final static String reservedWords[]= {"==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "++", "--", "abstract", "boolean", "break", "byte", "case", "catch", "char", "class", "continue", "double", "else", "final", "float", "for", "if", "import", "instanceof", "int", "interface", "long", "new", "package", "private", "protected", "public", "return", "static", "switch", "synchronized", "throws", "try", "until", "void", "while"}; protected final void debugPrint(String S) { /*System.err.print(S); */ } protected final void debugPrint(double D) { /*System.err.print(D); */ } protected final void debugPrintln(String S) { /*System.err.println(S);*/ } static boolean isReservedWord(String val) { for (int i= 0; i< reservedWords.length; i++) { if (val.equals(reservedWords[i])) return true; } return false; } protected final void innerClass() { // we won't blow off inner class utterly FB.put(className, "HASINNERCLASS", sval); FB.put(methodName, "HASINNERCLASS", sval); if (className!= null) { innerClassName= className+ "#"+ sval; FB.put(innerClassName, "ISA", sval_prev); } state= S_NULL; } protected void parentClass() { state= S_NULL; if (sval_prev== null) return; if (innerClassName!= null) FB.put(innerClassName, sval_prev, sval); else FB.put(className, sval_prev, sval); if (sval_prev.equals("implements")) FB.put(sval, "ISA", "interface"); else if (sval_prev.equals("extends")) { FB.put(sval, "ISA", "class"); parentClassName= sval; } } protected void gotClassName() { String verb= "USESCLASS"; if ((sval_prev!= null) && sval_prev.equals("new")) verb= "CREATES"; FB.put(className, verb, sval); FB.put(methodName, verb, sval); state= S_NULL; } protected void gotFieldName(String _fieldName) { if ((_fieldName!= null) && !isReservedWord(_fieldName)) { if (className!= null) { FB.putAmbigSubjectChild(className+ "."+ _fieldName, "FIELD"); FB.put(className+ "."+ _fieldName, "INCLASS", className); FB.put(_fieldName, "INCLASS", className); } if (classFields!= null) classFields.put(_fieldName, sval_first); if (methodFields!= null) methodFields.put(_fieldName, sval_first); } sval_first= null; state= S_NULL; } protected void enterClass(String _className) { className= _className; classFields= new TreeMap(); methodFields= null; innerClassName= null; methodName= null; state= S_NULL; if (packageName!= null) { String fullClassName= packageName+ '.'+ className; // more specific name of class className= fullClassName; } FB.putAmbigSubjectChild(className, sval_prev); // so we can make sense of short name FB.put(className, "INFILE", currentFilename); } protected void leaveClass() { className= null; innerClassName= null; classFields= null; methodName= null; parentClassName= null; } final void callbackTokenTopLevel(int ttype, double nval, String _sval) { sval= _sval; switch (ttype) { case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: debugPrint(sval); if (state== S_XPCLASSNAME) { enterClass(sval); } else if (state== S_XPPARENTCLASSNAME) { parentClass(); } else if (state== S_XPPACKAGENAME) { if (packageName== null) packageName= sval; else { String parent= packageName; packageName+= sval; FB.putAmbigSubjectIsa(packageName, "package"); } //System.err.println(" package "+ packageName); } else if (sval.equals("class") || sval.equals("interface")) state= S_XPCLASSNAME; else if (sval.equals("extends") || sval.equals("implements")) { state= S_XPPARENTCLASSNAME; } else if (sval.equals("package")) { state= S_XPPACKAGENAME; packageName= null; } break; case StreamTokenizer.TT_NUMBER: debugPrint(nval); break; case ',': if (className!= null) state= S_XPPARENTCLASSNAME; break; case '{': debugPrint("{"); nesting++; sval_prev= null; break; case '}': System.err.println("NESTING ERROR "+ className); break; case '.': if (state== S_XPPACKAGENAME) { if (packageName== null) packageName= "."; // unlikely else packageName+= "."; } break; case ';': if (state== S_XPPACKAGENAME) { if (currentFilename== null) break; if (packageName== null) break; FB.put(currentFilename, "PACKAGE", packageName); FB.put(packageName, "ISA", "package"); state= S_NULL; } debugPrint(";"); sawEquals= false; sawDeclaration= false; state= S_NULL; break; default: char data[]= {(char)ttype}; debugPrint(new String(data)); //if (data[0]=='\'') { System.out.print(sval+ "'"); } //else if (data[0]=='"') { System.out.print(sval+ "\""); } break; } debugPrint(" "); sval_prev= sval; } protected void enterMethod(String _methodName) { boolean isConstructor= className.endsWith("."+ _methodName); if (className.equals(_methodName)) isConstructor= true; methodName= _methodName; if (className!= null) { if (!isConstructor) FB.put(className, "HASMETHODNAME", methodName); String fullMethodName= className+ '.'+ methodName; // more specific name of class methodName= fullMethodName; } if (!isConstructor) FB.putAmbigSubjectChild(methodName, "method"); FB.put(methodName, "INCLASS", className); FB.put(methodName, "INFILE", currentFilename); // someday let's differentiate among different parameter list forms, linked by MAYREFERTO // also someday let's note the type of return value methodFields= new TreeMap(); // local variables and their types } protected void leaveMethod() { methodName= null; methodFields= null; } void callbackTokenInClass(int ttype, double nval, String _sval) { sval= _sval; switch (ttype) { case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: debugPrint(sval); // some modifier keywords we want to COMPLETELY ignore if they begin a declaration. // no setting of sval_prev or ttype_prev, as if we never saw them: if ((ttype_prev== ';') || (ttype_prev== '{') || (ttype_prev== '}')) { if (sval.equals("final") || sval.equals("public") || sval.equals("protected") || sval.equals("private") || sval.equals("static") || sval.equals("synchronized")) { ttype= ttype_prev; return; } // (maybe someday set flags to record these as attributes of a class or method) } if (sval.equals("extends") || sval.equals("implements")) { if (innerClassName!= null) state= S_XPPARENTCLASSNAME; } else if (sval.equals("throws")) { state= S_XPTHROWSNAME; } else if (sval.equals("new")) { state= S_XPCLASSNAME; // expect classname to follow } else if (state== S_XPCLASSNAME) { gotClassName(); } else if (state== S_XPPARENTCLASSNAME) { parentClass(); // inner class } else if (state== S_XPTHROWSNAME) { if (methodName== null) break; FB.put(methodName, "throws", sval); FB.put(sval, "ISA", "Exception"); state= S_NULL; } else if (state== S_XPINNERCLASSNAME) { innerClass(); } else if (state== S_XPFIELDNAME) { sval_first= sval_prev; gotFieldName(sval); } else if (sval.equals("class") || sval.equals("interface")) { // inner class. state= S_XPINNERCLASSNAME; } else { if (isReservedWord(sval)) return; if (state== S_INARGLIST) { if ((ttype_prev== '(') || (ttype_prev== ',')) { FB.put(className, "PASSED", sval); FB.put(methodName, "PASSED", sval); state= S_XPFIELDNAME; } } else { if ((ttype_prev== ';') || (ttype_prev== '{') || (ttype_prev== '}')) { FB.put(className, "USESCLASS", sval); sval_first= sval; state= S_XPSECOND; } } } break; case StreamTokenizer.TT_NUMBER: debugPrint(nval); break; case '{': debugPrintln("{"); innerClassName= null; sval_prev= null; sawEquals= false; sawDeclaration= false; nesting++; break; case '}': nesting--; debugPrintln("}"); if (nesting== 0) // leaving class leaveClass(); sval_prev= null; sawEquals= false; sawDeclaration= false; state= S_NULL; break; case ';': debugPrint(";"); if (sawDeclaration) break; gotFieldName(sval_prev); sawEquals= false; sawDeclaration= false; state= S_NULL; break; case '=': debugPrint("="); sawEquals= true; if (sawDeclaration) break; // could be abstract method gotFieldName(sval_prev); sawDeclaration= true; break; case '(': // start of argument list to method debugPrint("("); if (sval_prev== null) break; if (sawEquals) break; enterMethod(sval_prev); state= S_INARGLIST; sawDeclaration= true; break; case ')': debugPrint(")"); state= S_NULL; break; default: char data[]= {(char)ttype}; debugPrint(new String(data)); //if (data[0]=='\'') { System.out.print(sval+ "'"); } //else if (data[0]=='"') { System.out.print(sval+ "\""); } break; } debugPrint(" "); sval_prev= sval; } void callbackTokenInMethod(int ttype, double nval, String _sval) { sval= _sval; switch (ttype) { case StreamTokenizer.TT_EOL: break; case StreamTokenizer.TT_WORD: debugPrint(sval); // some modifier keywords we want to COMPLETELY ignore if they begin a declaration. // no setting of sval_prev or ttype_prev, as if we never saw them: if ((ttype_prev== ';') || (ttype_prev== '{') || (ttype_prev== '}')) { if (sval.equals("final") || sval.equals("static")) { ttype= ttype_prev; return; } } if (state== S_XPINNERCLASSNAME) { innerClass(); } else if (state== S_SAWDOT) { String fieldParent= sval_beforedot; String fieldName= sval; //System.err.print(" "+ methodName+ " S_SAWDOT "); //System.err.print("fieldParent:"+ fieldParent+ " fieldName:"+ fieldName); if ((fieldParent!= null) && !isReservedWord(fieldName)) { if (FB.query(fieldParent, "ISA", "class") || FB.query(fieldParent, "ISA", "interface")) { FB.put(methodName, "ACCESSES", fieldParent+ "."+ fieldName); } else if ((methodFields!= null) && methodFields.containsKey(fieldParent)) FB.put(methodName, "ACCESSES", methodFields.get(fieldParent)+ "."+ fieldName); else if ((classFields!= null) && classFields.containsKey(fieldParent)) FB.put(methodName, "ACCESSES", classFields.get(fieldParent)+ "."+ fieldName); else FB.put(methodName, "ACCESSES", fieldName); // better than nothing } //System.err.println(); state= S_NULL; } else if (sval.equals("class") || sval.equals("interface")) { // inner class. state= S_XPINNERCLASSNAME; } else if (sval.equals("new")) { state= S_XPCLASSNAME; // expect classname to follow } else if (isReservedWord(sval)) { debugPrint(" "); state= S_NULL; } else if (state== S_XPCLASSNAME) { gotClassName(); } else if (state== S_XPSECOND) { // probably a variable name. preceding would be its type FB.put(className, "USESCLASS", sval_prev); FB.put(methodName, "USESCLASS", sval_prev); gotFieldName(sval); } else if ((ttype_prev== ';') || (ttype_prev== '{') || (ttype_prev== '}')) { // looks like beginning of declaration sval_first= sval; state= S_XPSECOND; } else if (FB.query(className, "HASMETHODNAME", sval)) { FB.put(methodName, "ACCESSES", className+ "."+ sval); // method calling a sister method } else if (FB.query(parentClassName, "HASMETHODNAME", sval)) { FB.put(methodName, "ACCESSES", parentClassName+ "."+ sval); // calling an inherited method } else if (FB.query(sval, "ISA", "class") || FB.query(sval, "ISA", "interface")) { FB.put(className, "USESCLASS", sval); FB.put(methodName, "USESCLASS", sval); } break; case StreamTokenizer.TT_NUMBER: debugPrint(nval); if (state== S_XPSECOND) state= S_NULL; break; case '.': state= S_SAWDOT; if (sval_prev!= null) sval_beforedot= sval_prev; debugPrint("."); break; case '{': state= S_NULL; innerClassName= null; debugPrint("{"); sval_prev= null; nesting++; break; case '}': state= S_NULL; debugPrint("}"); nesting--; if (nesting== 0) // leaving class leaveMethod(); sval_prev= null; break; default: state= S_NULL; char data[]= {(char)ttype}; debugPrint(new String(data)); //if (data[0]=='\'') { System.out.print(sval+ "'"); } //else if (data[0]=='"') { System.out.print(sval+ "\""); } break; } debugPrint(" "); sval_prev= sval; } void callbackToken(int ttype, double nval, String sval) { if (nesting<= 0) { callbackTokenTopLevel(ttype, nval, sval); return; } if ((nesting== 1) && (className!= null)) { callbackTokenInClass(ttype, nval, sval); return; } if ((nesting> 1) && (methodName!= null)) { callbackTokenInMethod(ttype, nval, sval); return; } switch (ttype) { case '{': nesting++; break; case '}': nesting--; break; } } void listResults() { FB.listResults(); } void givenFilename(String filename) throws IOException { currentFilename= null; packageName= null; className= null; if (topName== null) topName= filename; File f= new File(filename); if (f.isDirectory()) { File[] list= f.listFiles(); int i; for (i=0; i< list.length; i++) { String filespec= list[i].getPath(); // recurse to traverse subdirectories givenFilename(filespec); } } else if (filename.endsWith(".java")) { System.err.println("Parsing: "+ filename); currentFilename= filename; FB.put(currentFilename, "ISA", "sourcefile"); super.givenFilename(filename); currentFilename= null; } } public void FactbaseToHtml() { Vector ofInterest= new Vector(); ofInterest.add("accept"); ofInterest.add("class"); ofInterest.add("getInputStream"); ofInterest.add("getOutputStream"); ofInterest.add("interface"); ofInterest.add("listen"); ofInterest.add("main"); ofInterest.add("method"); ofInterest.add("package"); ofInterest.add("sendError"); ofInterest.add("sendStatus"); ofInterest.add("sourcefile"); ofInterest.add("Applet"); ofInterest.add("BodyTag"); ofInterest.add("BodyTagSupport"); ofInterest.add("Canvas"); ofInterest.add("Class"); ofInterest.add("Component"); ofInterest.add("Connection"); ofInterest.add("Container"); ofInterest.add("DesktopManager"); ofInterest.add("Enumeration"); ofInterest.add("Event"); ofInterest.add("Exception"); ofInterest.add("File"); ofInterest.add("FileInputStream"); ofInterest.add("FileOutputStream"); ofInterest.add("Hashtable"); ofInterest.add("HttpServlet"); ofInterest.add("HttpServletRequest"); ofInterest.add("HttpServletResponse"); ofInterest.add("HttpSession"); ofInterest.add("HttpURLCOnnection"); ofInterest.add("InetAddress"); ofInterest.add("InputStream"); ofInterest.add("Iterator"); ofInterest.add("JDesktopPane"); ofInterest.add("LayoutManager"); ofInterest.add("Object"); ofInterest.add("ORB"); ofInterest.add("OutputStream"); ofInterest.add("PrintWriter"); ofInterest.add("Request"); ofInterest.add("ResultSet"); ofInterest.add("Runnable"); ofInterest.add("Runtime"); ofInterest.add("ServerRequest"); ofInterest.add("ServerSocket"); ofInterest.add("Servlet"); ofInterest.add("ServletContext"); ofInterest.add("ServletRequest"); ofInterest.add("ServletResponse"); ofInterest.add("Socket"); ofInterest.add("Statement"); ofInterest.add("System"); ofInterest.add("Tag"); ofInterest.add("TagSupport"); ofInterest.add("TreeMap"); ofInterest.add("URL"); ofInterest.add("URLCOnnection"); new FactbaseToHtml(FB, topName, ofInterest); } public static void main(String[] args) { try { JavaParser me= new JavaParser(); me.givenFilename(args[0]); //me.listResults(); me.FactbaseToHtml(); } catch(Exception e) { e.printStackTrace(); } } }