001: /*
002: $Id: CSTNode.java 4032 2006-08-30 07:18:49Z mguillem $
003:
004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:
006: Redistribution and use of this software and associated documentation
007: ("Software"), with or without modification, are permitted provided
008: that the following conditions are met:
009:
010: 1. Redistributions of source code must retain copyright
011: statements and notices. Redistributions must also contain a
012: copy of this document.
013:
014: 2. Redistributions in binary form must reproduce the
015: above copyright notice, this list of conditions and the
016: following disclaimer in the documentation and/or other
017: materials provided with the distribution.
018:
019: 3. The name "groovy" must not be used to endorse or promote
020: products derived from this Software without prior written
021: permission of The Codehaus. For written permission,
022: please contact info@codehaus.org.
023:
024: 4. Products derived from this Software may not be called "groovy"
025: nor may "groovy" appear in their names without prior written
026: permission of The Codehaus. "groovy" is a registered
027: trademark of The Codehaus.
028:
029: 5. Due credit should be given to The Codehaus -
030: http://groovy.codehaus.org/
031:
032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043: OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: */
046:
047: package org.codehaus.groovy.syntax;
048:
049: import org.codehaus.groovy.GroovyBugError;
050: import org.codehaus.groovy.syntax.Token;
051: import org.codehaus.groovy.syntax.Types;
052: import org.codehaus.groovy.syntax.Reduction;
053:
054: import java.io.StringWriter;
055: import java.io.PrintWriter;
056:
057: /**
058: * An abstract base class for nodes in the concrete syntax tree that is
059: * the result of parsing. Note that the CSTNode is inextricably linked
060: * with the Token in that every CSTNode has a Token as it's root.
061: *
062: * @see antlr.Parser
063: * @see Token
064: * @see org.codehaus.groovy.syntax.Reduction
065: * @see org.codehaus.groovy.syntax.Types
066: *
067: * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
068: * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
069: *
070: * @version $Id: CSTNode.java 4032 2006-08-30 07:18:49Z mguillem $
071: */
072:
073: public abstract class CSTNode {
074:
075: //---------------------------------------------------------------------------
076: // NODE IDENTIFICATION AND MEANING
077:
078: /**
079: * Returns the meaning of this node. If the node isEmpty(), returns
080: * the type of Token.NULL.
081: */
082:
083: public int getMeaning() {
084: return getRoot(true).getMeaning();
085: }
086:
087: /**
088: * Sets the meaning for this node (and it's root Token). Not
089: * valid if the node isEmpty(). Returns the node, for convenience.
090: */
091:
092: public CSTNode setMeaning(int meaning) {
093: getRoot().setMeaning(meaning);
094: return this ;
095: }
096:
097: /**
098: * Returns the actual type of the node. If the node isEmpty(), returns
099: * the type of Token.NULL.
100: */
101:
102: public int getType() {
103: return getRoot(true).getType();
104: }
105:
106: /**
107: * Returns true if the node can be coerced to the specified type.
108: */
109:
110: public boolean canMean(int type) {
111: return Types.canMean(getMeaning(), type);
112: }
113:
114: /**
115: * Returns true if the node's meaning matches the specified type.
116: */
117:
118: public boolean isA(int type) {
119: return Types.ofType(getMeaning(), type);
120: }
121:
122: /**
123: * Returns true if the node's meaning matches any of the specified types.
124: */
125:
126: public boolean isOneOf(int[] types) {
127: int meaning = getMeaning();
128: for (int i = 0; i < types.length; i++) {
129: if (Types.ofType(meaning, types[i])) {
130: return true;
131: }
132: }
133:
134: return false;
135: }
136:
137: /**
138: * Returns true if the node's meaning matches all of the specified types.
139: */
140:
141: public boolean isAllOf(int[] types) {
142: int meaning = getMeaning();
143: for (int i = 0; i < types.length; i++) {
144: if (!Types.ofType(meaning, types[i])) {
145: return false;
146: }
147: }
148:
149: return true;
150: }
151:
152: /**
153: * Returns the first matching meaning of the specified types.
154: * Returns Types.UNKNOWN if there are no matches.
155: */
156:
157: public int getMeaningAs(int[] types) {
158:
159: for (int i = 0; i < types.length; i++) {
160: if (isA(types[i])) {
161: return types[i];
162: }
163: }
164:
165: return Types.UNKNOWN;
166: }
167:
168: //---------------------------------------------------------------------------
169: // TYPE SUGAR
170:
171: /**
172: * Returns true if the node matches the specified type. Effectively
173: * a synonym for <code>isA()</code>. Missing nodes are Token.NULL.
174: */
175:
176: boolean matches(int type) {
177: return isA(type);
178: }
179:
180: /**
181: * Returns true if the node and it's first child match the specified
182: * types. Missing nodes are Token.NULL.
183: */
184:
185: boolean matches(int type, int child1) {
186: return isA(type) && get(1, true).isA(child1);
187: }
188:
189: /**
190: * Returns true if the node and it's first and second child match the
191: * specified types. Missing nodes are Token.NULL.
192: */
193:
194: boolean matches(int type, int child1, int child2) {
195: return matches(type, child1) && get(2, true).isA(child2);
196: }
197:
198: /**
199: * Returns true if the node and it's first three children match the
200: * specified types. Missing nodes are Token.NULL.
201: */
202:
203: boolean matches(int type, int child1, int child2, int child3) {
204: return matches(type, child1, child2)
205: && get(3, true).isA(child3);
206: }
207:
208: /**
209: * Returns true if the node an it's first four children match the
210: * specified types. Missing nodes have type Types.NULL.
211: */
212:
213: boolean matches(int type, int child1, int child2, int child3,
214: int child4) {
215: return matches(type, child1, child2, child3)
216: && get(4, true).isA(child4);
217: }
218:
219: //---------------------------------------------------------------------------
220: // MEMBER ACCESS
221:
222: /**
223: * Returns true if the node is completely empty (no root, even).
224: */
225:
226: public boolean isEmpty() {
227: return false;
228: }
229:
230: /**
231: * Returns the number of elements in the node (including root).
232: */
233:
234: public abstract int size();
235:
236: /**
237: * Returns true if the node has any non-root elements.
238: */
239:
240: public boolean hasChildren() {
241: return children() > 0;
242: }
243:
244: /**
245: * Returns the number of non-root elements in the node.
246: */
247:
248: public int children() {
249: int size = size();
250: if (size > 1) {
251: return size - 1;
252: }
253: return 0;
254: }
255:
256: /**
257: * Returns the specified element, or null.
258: */
259:
260: public abstract CSTNode get(int index);
261:
262: /**
263: * Returns the specified element, or Token.NULL if
264: * safe is set and the specified element is null (or doesn't
265: * exist).
266: */
267:
268: public CSTNode get(int index, boolean safe) {
269: CSTNode element = get(index);
270:
271: if (element == null && safe) {
272: element = Token.NULL;
273: }
274:
275: return element;
276: }
277:
278: /**
279: * Returns the root of the node. By convention, all nodes have
280: * a Token as the first element (or root), which indicates the type
281: * of the node. May return null if the node <code>isEmpty()</code>.
282: */
283:
284: public abstract Token getRoot();
285:
286: /**
287: * Returns the root of the node, the Token that indicates it's
288: * type. Returns a Token.NULL if safe and the actual root is null.
289: */
290:
291: public Token getRoot(boolean safe) {
292: Token root = getRoot();
293:
294: if (root == null && safe) {
295: root = Token.NULL;
296: }
297:
298: return root;
299: }
300:
301: /**
302: * Returns the text of the root. Uses <code>getRoot(true)</code>
303: * to get the root, so you will only receive null in return if the
304: * root token returns it.
305: */
306:
307: public String getRootText() {
308: Token root = getRoot(true);
309: return root.getText();
310: }
311:
312: /**
313: * Returns a description of the node.
314: */
315:
316: public String getDescription() {
317: return Types.getDescription(getMeaning());
318: }
319:
320: /**
321: * Returns the starting line of the node. Returns -1
322: * if not known.
323: */
324:
325: public int getStartLine() {
326: return getRoot(true).getStartLine();
327: }
328:
329: /**
330: * Returns the starting column of the node. Returns -1
331: * if not known.
332: */
333:
334: public int getStartColumn() {
335: return getRoot(true).getStartColumn();
336: }
337:
338: /**
339: * Marks the node a complete expression. Not all nodes support
340: * this operation!
341: */
342:
343: public void markAsExpression() {
344: throw new GroovyBugError(
345: "markAsExpression() not supported for this CSTNode type");
346: }
347:
348: /**
349: * Returns true if the node is a complete expression.
350: */
351:
352: public boolean isAnExpression() {
353: return isA(Types.SIMPLE_EXPRESSION);
354: }
355:
356: //---------------------------------------------------------------------------
357: // OPERATIONS
358:
359: /**
360: * Adds an element to the node. Returns the element for convenience.
361: * Not all nodes support this operation!
362: */
363:
364: public CSTNode add(CSTNode element) {
365: throw new GroovyBugError(
366: "add() not supported for this CSTNode type");
367: }
368:
369: /**
370: * Adds all children of the specified node to this one. Not all
371: * nodes support this operation!
372: */
373:
374: public void addChildrenOf(CSTNode of) {
375: for (int i = 1; i < of.size(); i++) {
376: add(of.get(i));
377: }
378: }
379:
380: /**
381: * Sets an element node in at the specified index. Returns the element
382: * for convenience. Not all nodes support this operation!
383: */
384:
385: public CSTNode set(int index, CSTNode element) {
386: throw new GroovyBugError(
387: "set() not supported for this CSTNode type");
388: }
389:
390: /**
391: * Creates a <code>Reduction</code> from this node. Returns self if the
392: * node is already a <code>Reduction</code>.
393: */
394:
395: public abstract Reduction asReduction();
396:
397: //---------------------------------------------------------------------------
398: // STRING CONVERSION
399:
400: /**
401: * Formats the node as a <code>String</code> and returns it.
402: */
403:
404: public String toString() {
405: StringWriter string = new StringWriter();
406: write(new PrintWriter(string));
407:
408: string.flush();
409: return string.toString();
410: }
411:
412: /**
413: * Formats the node and writes it to the specified <code>Writer</code>.
414: */
415:
416: public void write(PrintWriter writer) {
417: write(writer, "");
418: }
419:
420: /**
421: * Formats the node and writes it to the specified <code>Writer</code>.
422: * The indent is prepended to each output line, and is increased for each
423: * recursion.
424: */
425:
426: protected void write(PrintWriter writer, String indent) {
427: writer.print("(");
428:
429: if (!isEmpty()) {
430: Token root = getRoot(true);
431: int type = root.getType();
432: int meaning = root.getMeaning();
433:
434: //
435: // Display our type, text, and (optional) meaning
436:
437: writer.print(Types.getDescription(type));
438:
439: if (meaning != type) {
440: writer.print(" as ");
441: writer.print(Types.getDescription(meaning));
442: }
443:
444: if (getStartLine() > -1) {
445: writer.print(" at " + getStartLine() + ":"
446: + getStartColumn());
447: }
448:
449: String text = root.getText();
450: int length = text.length();
451: if (length > 0) {
452: writer.print(": ");
453: if (length > 40) {
454: text = text.substring(0, 17) + "..."
455: + text.substring(length - 17, length);
456: }
457:
458: writer.print(" \"");
459: writer.print(text);
460: writer.print("\" ");
461: } else if (children() > 0) {
462: writer.print(": ");
463: }
464:
465: //
466: // Recurse to display the children.
467:
468: int count = size();
469: if (count > 1) {
470: writer.println("");
471:
472: String indent1 = indent + " ";
473: String indent2 = indent + " ";
474: for (int i = 1; i < count; i++) {
475: writer.print(indent1);
476: writer.print(i);
477: writer.print(": ");
478:
479: get(i, true).write(writer, indent2);
480: }
481:
482: writer.print(indent);
483: }
484: }
485:
486: if (indent.length() > 0) {
487: writer.println(")");
488: } else {
489: writer.print(")");
490: }
491: }
492: }
|