001: /*
002: * Project: BeautyJ - Customizable Java Source Code Transformer
003: * Class: de.gulden.util.javasource.Code
004: * Version: 1.1
005: *
006: * Date: 2004-09-29
007: *
008: * Note: Contains auto-generated Javadoc comments created by BeautyJ.
009: *
010: * This is licensed under the GNU General Public License (GPL)
011: * and comes with NO WARRANTY. See file license.txt for details.
012: *
013: * Author: Jens Gulden
014: * Email: beautyj@jensgulden.de
015: */
016:
017: package de.gulden.util.javasource;
018:
019: import de.gulden.util.javasource.jjt.Node;
020: import de.gulden.util.javasource.jjt.*;
021: import de.gulden.util.xml.XMLToolbox;
022: import de.gulden.util.Toolbox;
023: import javax.xml.parsers.*;
024: import org.w3c.dom.*;
025: import java.io.*;
026: import java.util.*;
027:
028: /**
029: * Represents code in a method/constructor body, and in a static class initializer.
030: *
031: * @author Jens Gulden
032: * @version 1.1
033: */
034: public class Code extends Implementation {
035:
036: // ------------------------------------------------------------------------
037: // --- field ---
038: // ------------------------------------------------------------------------
039:
040: /**
041: * The raw code string.
042: */
043: protected String raw;
044:
045: // ------------------------------------------------------------------------
046: // --- constructor ---
047: // ------------------------------------------------------------------------
048:
049: /**
050: * Creates a new instance of Code.
051: */
052: public Code() {
053: super ();
054: }
055:
056: // ------------------------------------------------------------------------
057: // --- methods ---
058: // ------------------------------------------------------------------------
059:
060: /**
061: * Returns the raw code string.
062: */
063: public String getRaw() {
064: return raw;
065: }
066:
067: /**
068: * Sets the raw code string.
069: */
070: public void setRaw(String s) {
071: raw = s;
072: }
073:
074: /**
075: * Output this object as XML.
076: *
077: * @return The XML tag.
078: * @see #initFromXML
079: */
080: public Element buildXML(Document d) {
081: Element e = d.createElement("code");
082: String raw = getRaw();
083: e.appendChild(d.createTextNode(raw));
084: return e;
085: }
086:
087: /**
088: * Initialize this object from XML.
089: *
090: * @param element The XML tag.
091: * @throws IOException if an i/o error occurs
092: */
093: public void initFromXML(Element element) throws IOException {
094: raw = XMLToolbox.getText(element);
095: if (raw == null) {
096: raw = "";
097: }
098: }
099:
100: /**
101: * Initialize this object from parsed Java code.
102: *
103: * @param rootnode The corresponding node in the abstract syntax tree (AST).
104: */
105: void initFromAST(Node rootnode) {
106: // special way of invoking!!!
107: // rootnode is rootnode of the _declaration_ of the code block
108: // from there, following code will be rebuilt
109: // (this is necessary because due to lookahead in constructors, code start would be missing)
110: StringBuffer sb = new StringBuffer();
111: // find first "{"
112: Token t = rootnode.getStartToken();
113: while ((t.kind != ParserConstants.LBRACE)
114: && (t.kind != ParserConstants.ASSIGN)) // ASSIGN for field initializers
115: {
116: t = t.next;
117: }
118: Token endT;
119: if (t.kind == ParserConstants.LBRACE) // code block { ... }
120: {
121: endT = findCodeEnd(t);
122: } else // ASSIGN: field initializer
123: {
124: endT = rootnode.getEndToken();
125: }
126: raw = rootnode.getTextImage().getRange(t.beginLine,
127: t.beginColumn, endT.endLine, endT.endColumn);
128: raw = raw.trim();
129: raw = raw.substring(1, raw.length() - 1); // remove { } or = ;
130: // remove left part if only blanks before first \n:
131: int npos = raw.indexOf('\n');
132: if (npos != -1) {
133: String left = raw.substring(0, npos);
134: if (left.trim().length() == 0) {
135: raw = raw.substring(npos + 1);
136: }
137: }
138: if (raw.indexOf("...fields") != -1) {
139: System.out.println("AAA");
140: }
141: raw = unindent(raw);
142: raw = SourceParser.workaroundRestoreUnicodeSingleChar(raw); // @see SourceParser#parsePass1()
143: }
144:
145: // ------------------------------------------------------------------------
146: // --- static methods ---
147: // ------------------------------------------------------------------------
148:
149: static Token findCodeEnd(Token t) {
150: int cnt = 1;
151: while (cnt > 0) {
152: t = t.next;
153: switch (t.kind) {
154: case ParserConstants.LBRACE:
155: cnt++;
156: break;
157: case ParserConstants.RBRACE:
158: cnt--;
159: break;
160: }
161: }
162: return t;
163: }
164:
165: static Token findCodeEndInitializer(Token t) {
166: while ((t.kind != ParserConstants.SEMICOLON)
167: && (t.kind != ParserConstants.COMMA)) {
168: t = t.next;
169: if (t.kind == ParserConstants.LBRACE) {
170: t = findCodeEnd(t).next;
171: }
172: }
173: return t;
174: }
175:
176: /**
177: * Creates an unindented representation of a code block by shifting all lines to the left so that
178: * the first line will start at column 0. All other lines are shifted relative to the shift of the first line.
179: * The specified code block is expected to be passed without enclosing braces.
180: *
181: * @return unindented code
182: */
183: static String unindent(String code) {
184: List l = Toolbox.getLines(code);
185: StringBuffer sb = new StringBuffer();
186: String nl = System.getProperty("line.separator");
187: int shift = -1;
188: for (ListIterator it = l.listIterator(); it.hasNext();) {
189: String line = (String) it.next();
190: if (line.trim().length() == 0) { // blank line
191: if (it.hasNext()) { // skip last line if empty, it is just the remaining part until '}'
192: sb.append(nl);
193: }
194: } else {
195: String newLine;
196: if (shift == -1) { // first line
197: newLine = Toolbox.trimLeft(line);
198: shift = line.length() - newLine.length();
199: } else {
200: if (line.length() > shift) {
201: String cutoff = line.substring(0, shift);
202: if (cutoff.trim().length() == 0) { // no content would be cut off by shifting: normal shift
203: newLine = line.substring(shift);
204: } else {
205: newLine = Toolbox.trimLeft(line); // otherwise just trim all left whitespace, original relative shift to first line will be lost
206: }
207: } else {
208: newLine = Toolbox.trimLeft(line); // otherwise just trim all left whitespace, original relative shift to first line will be lost
209: }
210: }
211: sb.append(Toolbox.trimRight(newLine));
212: if (it.hasNext() /*&& (
213: ( !
214: ( ((String)it.next()).trim().length()==0 ) && ( !it.hasNext() ) // append new line only if not last line or line before an empty last line
215: )
216: & ( dummy(it.previous()) )
217: ) */) {
218: sb.append(nl);
219: }
220: }
221: }
222: String s = sb.toString();
223: if (s.endsWith(nl)) { // truncate very last line-break, if there (very last blank line is rest until '}' and will be auto-generated anyway)
224: s = s.substring(0, s.length() - nl.length());
225: }
226: return s;
227: }
228:
229: private static boolean dummy(Object o) {
230: // (allows to call it.previous()) from within boolean expression
231: return true;
232: }
233:
234: } // end Code
|