001: package com.avaje.util.codegen;
002:
003: import java.util.ArrayList;
004: import java.util.Iterator;
005: import java.util.LinkedHashMap;
006: import java.util.StringTokenizer;
007:
008: import com.avaje.lib.util.StringHelper;
009:
010: public class SourceCode {
011:
012: String crnl = "\r\n";
013:
014: /**
015: * The line where the package declaration is on.
016: */
017: int packageLine = -1;
018:
019: /**
020: * The line where the class declaration is on.
021: */
022: Class classDeclaration;
023:
024: /**
025: * Mapped by property name.
026: */
027: LinkedHashMap propertyMap = new LinkedHashMap();
028:
029: /**
030: * Mapped by method name.
031: */
032: LinkedHashMap methodMap = new LinkedHashMap();
033:
034: /**
035: * The import types mapped.
036: */
037: ArrayList importList = new ArrayList();
038:
039: /**
040: * The source code as a list of String
041: */
042: ArrayList lineList = new ArrayList();
043:
044: /**
045: * list of inner classes
046: */
047: ArrayList innerClassList = new ArrayList();
048:
049: /**
050: * The current method being read.
051: */
052: Method lastMethod;
053:
054: /**
055: * The current Inner class being read.
056: */
057: Class lastInnerClass;
058:
059: /**
060: * Set to true when started a method or inner class.
061: */
062: boolean isInBlock = false;
063:
064: /**
065: * The depth of braces used to determine when a method or inner class
066: * finishes.
067: */
068: int blockDepth = 0;
069:
070: /**
071: * Used to index the lines read from the file.
072: */
073: int lineIndex = 0;
074:
075: int lastBlockEnd = 0;
076:
077: boolean searchForClassOpenBracket = false;
078:
079: boolean debug = false;
080:
081: /**
082: * Iterator of imports as String.
083: */
084: public Iterator imports() {
085: return importList.iterator();
086: }
087:
088: /**
089: * The class declaration.
090: */
091: public String getClassDeclaration() {
092: if (classDeclaration == null) {
093: return null;
094: }
095: return classDeclaration.getCode();
096: }
097:
098: public String getClassComment() {
099: if (classDeclaration == null) {
100: return null;
101: }
102: return classDeclaration.getComment();
103: }
104:
105: /**
106: * The method names as an Iterator of String.
107: */
108: public Iterator methodNames() {
109: return methodMap.keySet().iterator();
110: }
111:
112: /**
113: * Return the comments and code for a method.
114: */
115: public String getMethod(String methodName) {
116: Method method = (Method) methodMap.get(methodName);
117: if (method != null) {
118: return getCode(method.prevBlockEnd + 1, method.endLine);
119: }
120: return null;
121: }
122:
123: /**
124: * The property names as an iterator of String.
125: */
126: public Iterator propertyNames() {
127: return propertyMap.keySet().iterator();
128: }
129:
130: /**
131: * Return the type of the property. Null if the property doesn't exist.
132: */
133: public String getPropertyType(String propertyName) {
134: Property p = (Property) propertyMap.get(propertyName);
135: if (p != null) {
136: return p.type;
137: }
138: return null;
139: }
140:
141: public String getPropertyCode(String propertyName) {
142: Property p = (Property) propertyMap.get(propertyName);
143: if (p != null) {
144: return getCode(p.prevBlockEnd + 1, p.startLine);
145: }
146: return null;
147: }
148:
149: public String innerClassCode() {
150: StringBuffer sb = new StringBuffer();
151:
152: Iterator i = innerClassList.iterator();
153: while (i.hasNext()) {
154: Class iclass = (Class) i.next();
155: String code = getCode(0, iclass.endLine);
156: sb.append(code);
157: sb.append(crnl);
158: }
159: if (sb.length() == 0) {
160: return null;
161: }
162: return sb.toString();
163: }
164:
165: // private String getComment(int linePos) {
166: // String comment = "";
167: // for (int i = linePos-1; i >= 0; i--) {
168: // Line line = getLine(i);
169: // if (!line.isComment){
170: // //System.out.println("break on ["+line+"]");
171: // break;
172: // } else {
173: // comment = line.content + crnl + comment;
174: // }
175: // }
176: // return comment;
177: // }
178:
179: private String getCode(int minPos, int linePos) {
180: String code = null;
181: int i = linePos;
182: for (; i >= minPos; i--) {
183: Line line = getLine(i);
184: if (line.isComment) {
185: // System.out.println("break on ["+line+"]");
186: break;
187: } else {
188: if (code != null) {
189: code = line.content + crnl + code;
190: } else {
191: code = line.content;
192: }
193: }
194: }
195: for (; i >= minPos; i--) {
196: Line line = getLine(i);
197: if (!line.isComment) {
198: // System.out.println("break on ["+line+"]");
199: break;
200: } else {
201: if (code != null) {
202: code = line.content + crnl + code;
203: } else {
204: code = line.content;
205: }
206: }
207: }
208: return code;
209: }
210:
211: /**
212: * Return any comments or lines prior to the package declaration.
213: */
214: public String getPackageComment() {
215: if (packageLine > 0) {
216: StringBuffer sb = new StringBuffer();
217: for (int i = 0; i < packageLine; i++) {
218: sb.append(getLineContent(i));
219: if (i < packageLine - 1) {
220: sb.append(crnl);
221: }
222: }
223: return sb.toString();
224: }
225: return null;
226: }
227:
228: public void addCode(String code) {
229: // System.out.println("Code > "+code);
230: Line line = new Line(lineIndex++, code, false);
231: lineList.add(line);
232:
233: code = code.trim();
234: if (code.startsWith("package ")) {
235: hitPackage(line);
236:
237: } else if (code.startsWith("import")) {
238: addImport(code, line);
239:
240: } else if (code.startsWith("//")) {
241: // line comment.. this may be lost...
242:
243: } else if (classDeclaration == null) {
244: // && code.indexOf(" class ") > -1
245: hitStartOfClass(line);
246:
247: } else {
248: // look for properties and method blocks...
249: codeParse(code, line);
250: }
251: }
252:
253: private void addPropertyDeclaration(Line line, String type,
254: String name) {
255: if (debug) {
256: System.out.println("Property> " + name);
257: }
258: Property p = new Property(line.index, type, name);
259: p.prevBlockEnd = lastBlockEnd;
260:
261: propertyMap.put(name, p);
262: lastBlockEnd = line.index;
263: }
264:
265: private void addMethodStart(Line line, String methodName) {
266: if (debug) {
267: System.out.println("Method> " + methodName);
268: }
269: lastMethod = new Method(line.index, methodName);
270: lastMethod.prevBlockEnd = lastBlockEnd;
271: methodMap.put(methodName, lastMethod);
272: }
273:
274: private void addInnerClassStart(Line line) {//, String[] parts) {
275: lastInnerClass = new Class(line.index, lastBlockEnd);
276: innerClassList.add(lastInnerClass);
277: }
278:
279: private void addBlockEnd(Line line) {
280: if (lastMethod == null && lastInnerClass == null) {
281: throw new RuntimeException(
282: "No current innerClass or method?");
283: }
284: lastBlockEnd = line.index;
285: if (lastMethod != null) {
286: lastMethod.endLine = lastBlockEnd;
287: lastMethod = null;
288:
289: } else {
290: lastInnerClass.endLine = lastBlockEnd;
291: lastInnerClass = null;
292: }
293: }
294:
295: private void hitPackage(Line line) {
296: if (debug) {
297: System.out.println("package> " + line);
298: }
299: packageLine = line.index;
300: }
301:
302: private void hitStartOfClass(Line line) {
303: if (debug) {
304: System.out.println("StartOfClass> " + line);
305: }
306: classDeclaration = new Class(line.index, lastBlockEnd + 1);
307:
308: if (line.content.indexOf("{") == -1) {
309: searchForClassOpenBracket = true;
310: } else {
311: classDeclaration.endLine = line.index;
312: }
313: lastBlockEnd = line.index;
314: }
315:
316: private void hitEndOfClass(Line line) {
317: if (debug) {
318: System.out.println("EndOfClass> " + line);
319: }
320: }
321:
322: /**
323: * This is a property declaration.
324: */
325: private boolean determineProperty(String code, Line line,
326: int semiPos) {
327: String searchCode = code.substring(0, semiPos);
328: int eqPos = searchCode.indexOf('=');
329: if (eqPos > -1) {
330: searchCode = searchCode.substring(0, eqPos);
331:
332: }
333: searchCode = searchCode.trim();
334: // get the last 2 tokens...
335: StringTokenizer t = new StringTokenizer(searchCode, " ", false);
336: int tc = t.countTokens();
337: for (int i = 0; i < tc - 2; i++) {
338: t.nextToken();
339: }
340: String type = t.nextToken();
341: String name = t.nextToken();
342:
343: // must be a property
344: addPropertyDeclaration(line, type, name);
345: return false;
346: }
347:
348: /**
349: * Strip out annotations and continue search.
350: */
351: private boolean stripAnnotation(String code, Line line,
352: int bracketPos) {
353:
354: // this is an Annotation like @Column(
355: // trim of the leading annotation text
356: // and continue looking...
357: int closePos = code.indexOf(')', bracketPos);
358: if (closePos == -1) {
359: String m = "Expection ) in [" + code + "] after ["
360: + bracketPos + "]";
361: throw new RuntimeException(m);
362: }
363: if (closePos + 1 >= code.length()) {
364: // Annotation on its own separate line
365: return false;
366: }
367: code = code.substring(closePos + 1);
368: return determineCodeType(code, line);
369: }
370:
371: /**
372: * Perhaps found a Method but need to check for annotations or property assignment.
373: */
374: private boolean determineMethod(String code, Line line,
375: int bracketPos) {
376: // check for Annotation like @Column(
377: int atPos = code.lastIndexOf('@', bracketPos);
378: if (atPos > -1) {
379: int atSpace = code.indexOf(' ', atPos);
380: if (atSpace > -1 && atSpace < bracketPos) {
381: // Annotation without a ( so we found a method
382:
383: } else {
384: // we found a bracket belonging to an Annotation
385: // strip it off and start again...
386: return stripAnnotation(code, line, bracketPos);
387: }
388: }
389:
390: // check for String name = new String();
391: int eqPos = code.indexOf('=');
392: if (eqPos > -1 && eqPos < bracketPos) {
393: // must be a property so chop off after =
394: return determineProperty(code, line, eqPos);
395: }
396:
397: // we have found a method
398: // find the method name
399: int spacePos = code.lastIndexOf(' ', bracketPos);
400: String methodName = code.substring(spacePos + 1, bracketPos);
401:
402: isInBlock = true;
403: addMethodStart(line, methodName);
404: return true;
405:
406: }
407:
408: /**
409: * Find a property, method, inner class or nothing of note.
410: */
411: private boolean determineCodeType(String code, Line line) {
412:
413: int parenPos = code.indexOf('{');
414: int bracketPos = code.indexOf('(');
415: int semiPos = code.indexOf(';');
416:
417: if (parenPos == -1 && bracketPos == -1 && semiPos == -1) {
418: // no method property or inner class
419: return false;
420: }
421: semiPos = semiPos == -1 ? 1000000 : semiPos;
422: bracketPos = bracketPos == -1 ? 1000000 : bracketPos;
423: parenPos = parenPos == -1 ? 1000000 : parenPos;
424:
425: if (semiPos < bracketPos && semiPos < parenPos) {
426: // can only be a property
427: return determineProperty(code, line, semiPos);
428: }
429:
430: if (bracketPos < parenPos && bracketPos < semiPos) {
431: // still not sure but perhaps a method
432: return determineMethod(code, line, bracketPos);
433: }
434:
435: // it must be a Inner Class
436: isInBlock = true;
437: addInnerClassStart(line);
438: return true;
439: }
440:
441: private void codeParse(String code, Line line) {
442: if (searchForClassOpenBracket) {
443: if (line.content.indexOf("{") > -1) {
444: searchForClassOpenBracket = false;
445: classDeclaration.endLine = line.index;
446: lastBlockEnd = line.index;
447: }
448: return;
449: }
450: if (!isInBlock) {
451: if (code.startsWith("}")) {
452: hitEndOfClass(line);
453: return;
454: }
455:
456: if (!determineCodeType(code, line)) {
457: return;
458: }
459:
460: // int pos = 0;
461: // String[] parts = new String[3];
462: // StringTokenizer t = new StringTokenizer(code, " ;=(", true);
463: // while (t.hasMoreTokens()) {
464: // String token = (String) t.nextToken();
465: // if (pos == 0 && token.equals("public") || token.equals("private")
466: // || token.equals("protected") || token.equals("final")) {
467: // // ignore
468: //
469: // } else if (token.equals(" ")) {
470: // // ignore
471: //
472: // } else if (token.equals("class")) {
473: // // method
474: // isInBlock = true;
475: // addInnerClassStart(line);//, parts);
476: // break;
477: //
478: // } else if (token.equals("(")) {
479: // // method
480: // isInBlock = true;
481: // addMethodStart(line, parts);
482: // break;
483: //
484: // } else if (token.equals(";") || token.equals("=")) {
485: // addPropertyDeclaration(line, parts);
486: // return;
487: //
488: // } else {
489: // parts[pos++] = token;
490: // }
491: // }
492: }
493:
494: int openCount = StringHelper.countOccurances(code, "{");
495: int closeCount = StringHelper.countOccurances(code, "}");
496: int newDepth = blockDepth + openCount - closeCount;
497: if (isInBlock && newDepth == 0) {
498: if (blockDepth == 0 && openCount == 0 && closeCount == 0) {
499: // ignore this
500: } else {
501: isInBlock = false;
502: addBlockEnd(line);
503: }
504: }
505: blockDepth = newDepth;
506: }
507:
508: private void addImport(String code, Line line) {
509: lastBlockEnd = line.index;
510: code = code.substring(6);
511: code = code.trim();
512: code = code.substring(0, code.length() - 1);
513: importList.add(code);
514: if (debug) {
515: System.out.println("Import> " + code);
516: }
517: }
518:
519: public void addComment(String comment) {
520: if (debug) {
521: System.out.println("Comment> " + comment);
522: }
523: lineList.add(new Line(lineIndex++, comment, true));
524: }
525:
526: private Line getLine(int index) {
527: return (Line) lineList.get(index);
528: }
529:
530: private String getLineContent(int index) {
531: Line line = getLine(index);
532: if (line != null) {
533: return line.content;
534: }
535: return null;
536: }
537:
538: private String getTheCode(int minPos, int linePos) {
539: String code = null;
540: int i = linePos;
541: for (; i >= minPos; i--) {
542: Line line = getLine(i);
543: if (line.isComment) {
544: // System.out.println("break on ["+line+"]");
545: break;
546: } else {
547: if (code != null) {
548: code = line.content + crnl + code;
549: } else {
550: code = line.content;
551: }
552: }
553: }
554: return code;
555: }
556:
557: private String getTheComments(int minPos, int linePos) {
558: String code = null;
559: for (int i = minPos; i <= linePos; i++) {
560: Line line = getLine(i);
561: if (!line.isComment) {
562: break;
563: } else {
564: if (code != null) {
565: code = code + crnl + line.content;
566: } else {
567: code = line.content;
568: }
569: }
570: }
571: return code;
572: }
573:
574: class Line {
575: String content;
576:
577: boolean isComment = false;
578:
579: int index;
580:
581: Line(int index, String content, boolean isComment) {
582: this .index = index;
583: this .content = content;
584: this .isComment = isComment;
585: }
586:
587: public String toString() {
588: return content;
589: }
590: }
591:
592: class Property {
593:
594: int prevBlockEnd;
595:
596: int startLine;
597:
598: String type;
599:
600: String name;
601:
602: Property(int startLine, String type, String name) {
603: this .startLine = startLine;
604: this .type = type;
605: this .name = name;
606: }
607: }
608:
609: class Method {
610:
611: int prevBlockEnd;
612:
613: int startLine;
614:
615: int endLine;
616:
617: //String returnType;
618:
619: String methodName;
620:
621: Method(int startLine, String methodName) {
622: this .startLine = startLine;
623: //this.returnType = returnType;
624: this .methodName = methodName;
625: }
626: }
627:
628: class Class {
629:
630: int prevBlockEnd;
631:
632: int startLine;
633:
634: int endLine;
635:
636: Class(int startLine, int prevBlockEnd) {
637: this .prevBlockEnd = prevBlockEnd;
638: this .startLine = startLine;
639: }
640:
641: public String getCode() {
642: return getTheCode(prevBlockEnd, endLine);
643: }
644:
645: public String getComment() {
646: return getTheComments(prevBlockEnd, startLine);
647: }
648: }
649:
650: }
|