[teiid-commits] teiid SVN: r2485 - in branches/7.1.x/engine/src: test/java/org/teiid/query/optimizer and 1 other directories.

teiid-commits at lists.jboss.org teiid-commits at lists.jboss.org
Mon Aug 23 15:19:46 EDT 2010


Author: shawkins
Date: 2010-08-23 15:19:45 -0400 (Mon, 23 Aug 2010)
New Revision: 2485

Modified:
   branches/7.1.x/engine/src/main/java/org/teiid/query/sql/visitor/SQLStringVisitor.java
   branches/7.1.x/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java
   branches/7.1.x/engine/src/test/java/org/teiid/query/parser/TestSetQueryParsing.java
Log:
TEIIDDES-567 updates to SQLStringVisitor logic for Teiid Designer.

Modified: branches/7.1.x/engine/src/main/java/org/teiid/query/sql/visitor/SQLStringVisitor.java
===================================================================
--- branches/7.1.x/engine/src/main/java/org/teiid/query/sql/visitor/SQLStringVisitor.java	2010-08-23 17:53:50 UTC (rev 2484)
+++ branches/7.1.x/engine/src/main/java/org/teiid/query/sql/visitor/SQLStringVisitor.java	2010-08-23 19:19:45 UTC (rev 2485)
@@ -24,11 +24,9 @@
 
 import static org.teiid.language.SQLConstants.Reserved.*;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 
 import org.teiid.core.types.DataTypeManager;
@@ -128,8 +126,9 @@
 import org.teiid.translator.SourceSystemFunctions;
 
 /**
- * <p>The SQLStringVisitor will visit a set of language objects and return the
- * corresponding SQL string representation. </p>
+ * <p>
+ * The SQLStringVisitor will visit a set of language objects and return the corresponding SQL string representation.
+ * </p>
  */
 public class SQLStringVisitor extends LanguageVisitor {
 
@@ -138,1099 +137,1010 @@
     private static final String BEGIN_HINT = "/*+"; //$NON-NLS-1$
     private static final String END_HINT = "*/"; //$NON-NLS-1$
     private static final char ID_ESCAPE_CHAR = '\"';
-    
-    private LinkedList<Object> parts = new LinkedList<Object>();
 
+    protected StringBuilder parts = new StringBuilder();
+
     /**
      * Helper to quickly get the parser string for an object using the visitor.
+     * 
      * @param obj Language object
      * @return String SQL String for obj
      */
-    public static final String getSQLString(LanguageObject obj) {
-        if(obj == null) {
-            return UNDEFINED; 
+    public static final String getSQLString( LanguageObject obj ) {
+        if (obj == null) {
+            return UNDEFINED;
         }
-        SQLStringVisitor visitor = new SQLStringVisitor();       
+        SQLStringVisitor visitor = new SQLStringVisitor();
         obj.acceptVisitor(visitor);
-    	return visitor.getSQLString();
+        return visitor.getSQLString();
     }
 
     /**
      * Retrieve completed string from the visitor.
+     * 
      * @return Complete SQL string for the visited nodes
      */
     public String getSQLString() {
-        StringBuilder output = new StringBuilder();
-        getSQLString(this.parts, output);
-        return output.toString();
+        return this.parts.toString();
     }
-    
-    public static void getSQLString(List<Object> parts, StringBuilder output) {
-        for (Object object : parts) {
-            if (object instanceof List) {
-                getSQLString((List<Object>)object, output);
-            } else {
-                output.append(object);
-            }
-        } 
-    }
-    
-    public List<Object> registerNode(LanguageObject obj) {
+
+    protected void visitNode( LanguageObject obj ) {
         if (obj == null) {
-            return Arrays.asList((Object)UNDEFINED);
+            append(UNDEFINED);
+            return;
         }
-        SQLStringVisitor visitor = new SQLStringVisitor();       
-        obj.acceptVisitor(visitor);
-        return visitor.parts;
+        obj.acceptVisitor(this);
     }
-    
-    public void replaceStringParts(Object[] parts) {
-        for (int i = 0; i < parts.length; i++) {
-            this.parts.add(parts[i]);
-        } 
+
+    protected void append( Object value ) {
+        this.parts.append(value);
     }
 
+    protected void beginClause( @SuppressWarnings("unused") int level ) {
+        append(SPACE);
+    }
+
     // ############ Visitor methods for language objects ####################
 
-    public void visit(BetweenCriteria obj) {
-        parts.add(registerNode(obj.getExpression()));
-        parts.add(SPACE);
-        
+    public void visit( BetweenCriteria obj ) {
+        visitNode(obj.getExpression());
+        append(SPACE);
+
         if (obj.isNegated()) {
-            parts.add(NOT);
-            parts.add(SPACE);
+            append(NOT);
+            append(SPACE);
         }
-        parts.add(BETWEEN);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getLowerExpression()));
+        append(BETWEEN);
+        append(SPACE);
+        visitNode(obj.getLowerExpression());
 
-        parts.add(SPACE);
-        parts.add(AND);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getUpperExpression()));
+        append(SPACE);
+        append(AND);
+        append(SPACE);
+        visitNode(obj.getUpperExpression());
     }
 
-    public void visit(CaseExpression obj) {
-        parts.add(CASE);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getExpression()) ); 
-        parts.add(SPACE);
+    public void visit( CaseExpression obj ) {
+        append(CASE);
+        append(SPACE);
+        visitNode(obj.getExpression());
+        append(SPACE);
 
         for (int i = 0; i < obj.getWhenCount(); i++) {
-            parts.add(WHEN);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getWhenExpression(i)) );
-            parts.add(SPACE);
-            parts.add(THEN);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getThenExpression(i)));
-            parts.add(SPACE);
+            append(WHEN);
+            append(SPACE);
+            visitNode(obj.getWhenExpression(i));
+            append(SPACE);
+            append(THEN);
+            append(SPACE);
+            visitNode(obj.getThenExpression(i));
+            append(SPACE);
         }
 
         if (obj.getElseExpression() != null) {
-            parts.add(ELSE);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getElseExpression()));
-            parts.add(SPACE);
+            append(ELSE);
+            append(SPACE);
+            visitNode(obj.getElseExpression());
+            append(SPACE);
         }
-        parts.add(END);
+        append(END);
     }
 
-    public void visit(CompareCriteria obj) {
+    public void visit( CompareCriteria obj ) {
         Expression leftExpression = obj.getLeftExpression();
-        Object leftPart = registerNode(leftExpression);
-
-        String operator = obj.getOperatorAsString();
-
+        visitNode(leftExpression);
+        append(SPACE);
+        append(obj.getOperatorAsString());
+        append(SPACE);
         Expression rightExpression = obj.getRightExpression();
-        Object rightPart = registerNode(rightExpression);
-
-        replaceStringParts(new Object[] { leftPart, SPACE, operator, SPACE, rightPart });
+        visitNode(rightExpression);
     }
 
-    public void visit(CompoundCriteria obj) {
+    public void visit( CompoundCriteria obj ) {
         // Get operator string
         int operator = obj.getOperator();
         String operatorStr = ""; //$NON-NLS-1$
-        if(operator == CompoundCriteria.AND) {
+        if (operator == CompoundCriteria.AND) {
             operatorStr = AND;
-        } else if(operator == CompoundCriteria.OR) {
+        } else if (operator == CompoundCriteria.OR) {
             operatorStr = OR;
         }
 
         // Get criteria
-        List subCriteria = obj.getCriteria();
+        List<Criteria> subCriteria = obj.getCriteria();
 
         // Build parts
-        if(subCriteria.size() == 1) {
+        if (subCriteria.size() == 1) {
             // Special case - should really never happen, but we are tolerant
-            Criteria firstChild = (Criteria) subCriteria.get(0);
-            replaceStringParts(new Object[] { registerNode(firstChild) });
+            Criteria firstChild = subCriteria.get(0);
+            visitNode(firstChild);
         } else {
-            // Magic formula - suppose you have 2 sub criteria, then the string
-            // has parts: (|x|)| |AND| |(|y|)
-            // Each sub criteria has 3 parts and each connector has 3 parts
-            // Number of connectors = number of sub criteria - 1
-            // # parts = 3n + 3c      ; c=n-1
-            //         = 3n + 3(n-1)
-            //         = 6n - 3
-            Object[] parts = new Object[(6*subCriteria.size())-3];
-
             // Add first criteria
-            Iterator iter = subCriteria.iterator();
-            Criteria crit = (Criteria) iter.next();
-            parts[0] = "("; //$NON-NLS-1$
-            parts[1] = registerNode(crit);
-            parts[2] = ")"; //$NON-NLS-1$
+            Iterator<Criteria> iter = subCriteria.iterator();
 
-            // Add rest of the criteria
-            for(int i=3; iter.hasNext(); i=i+6) {
-                // Add connector
-                parts[i] = SPACE;
-                parts[i+1] = operatorStr;
-                parts[i+2] = SPACE;
-
+            while (iter.hasNext()) {
                 // Add criteria
-                crit = (Criteria) iter.next();
-                parts[i+3] = "("; //$NON-NLS-1$
-                parts[i+4] = registerNode(crit);
-                parts[i+5] = ")"; //$NON-NLS-1$
-            }
+                Criteria crit = iter.next();
+                append(Tokens.LPAREN);
+                visitNode(crit);
+                append(Tokens.RPAREN);
 
-            replaceStringParts(parts);
+                if (iter.hasNext()) {
+                    // Add connector
+                    append(SPACE);
+                    append(operatorStr);
+                    append(SPACE);
+                }
+            }
         }
     }
 
-    public void visit(Delete obj) {
-		//add delete clause
-		parts.add(DELETE);
-		parts.add(SPACE);
-		//add from clause
-		parts.add(FROM);
-		parts.add(SPACE);
-		parts.add(registerNode(obj.getGroup()));
+    public void visit( Delete obj ) {
+        // add delete clause
+        append(DELETE);
+        append(SPACE);
+        // add from clause
+        append(FROM);
+        append(SPACE);
+        visitNode(obj.getGroup());
 
-		//add where clause
-		if(obj.getCriteria() != null) {
-			parts.add(SPACE);
-			parts.add(WHERE);
-			parts.add(SPACE);
-			parts.add(registerNode(obj.getCriteria()));
-		}
+        // add where clause
+        if (obj.getCriteria() != null) {
+            beginClause(0);
+            visitCriteria(WHERE, obj.getCriteria());
+        }
 
-		// Option clause
-		if(obj.getOption() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOption()));
-		}
+        // Option clause
+        if (obj.getOption() != null) {
+            beginClause(0);
+            visitNode(obj.getOption());
+        }
     }
-    
-    public void visit(DependentSetCriteria obj) {
-        parts.add(registerNode(obj.getExpression()));
 
+    public void visit( DependentSetCriteria obj ) {
+        visitNode(obj.getExpression());
+
         // operator and beginning of list
-        parts.add(SPACE);
+        append(SPACE);
         if (obj.isNegated()) {
-            parts.add(NOT);
-            parts.add(SPACE);
+            append(NOT);
+            append(SPACE);
         }
-        parts.add(IN);
-        parts.add(" (<dependent values>)"); //$NON-NLS-1$
+        append(IN);
+        append(" (<dependent values>)"); //$NON-NLS-1$
     }
 
-    public void visit(From obj) {
-        Object[] parts = null;
-        List clauses = obj.getClauses();
-        if(clauses.size() == 1) {
-            replaceStringParts(new Object[] {
-                FROM, SPACE,
-                registerNode( (FromClause) clauses.get(0) ) });
-        } else if(clauses.size() > 1) {
-            parts = new Object[2 + clauses.size() + (clauses.size()-1)];
+    public void visit( From obj ) {
+        append(FROM);
+        beginClause(1);
+        registerNodes(obj.getClauses(), 0);
+    }
 
-            // Add first clause
-            parts[0] = FROM;
-            parts[1] = SPACE;
-            Iterator clauseIter = clauses.iterator();
-            parts[2] = registerNode((FromClause) clauseIter.next());
-
-            // Add rest of the clauses
-            for(int i=3; clauseIter.hasNext(); i=i+2) {
-                parts[i] = ", "; //$NON-NLS-1$
-                parts[i+1] = registerNode((FromClause) clauseIter.next());
-            }
-
-            replaceStringParts(parts);
-        } else {
-            // Shouldn't happen, but being tolerant
-            replaceStringParts(new Object[] { FROM });
-        }
+    public void visit( GroupBy obj ) {
+        append(GROUP);
+        append(SPACE);
+        append(BY);
+        append(SPACE);
+        registerNodes(obj.getSymbols(), 0);
     }
 
-    public void visit(GroupBy obj) {
-        Object[] parts = null;
-        List symbols = obj.getSymbols();
-        if(symbols.size() == 1) {
-            replaceStringParts(new Object[] {
-                GROUP, SPACE, BY, SPACE,
-                registerNode( (Expression) symbols.get(0) ) });
-        } else if(symbols.size() > 1) {
-            parts = new Object[4 + symbols.size() + (symbols.size()-1)];
+    public void visit( Insert obj ) {
+        append(INSERT);
+        append(SPACE);
+        append(INTO);
+        append(SPACE);
+        visitNode(obj.getGroup());
 
-            // Add first clause
-            parts[0] = GROUP;
-            parts[1] = SPACE;
-            parts[2] = BY;
-            parts[3] = SPACE;
-            Iterator symbolIter = symbols.iterator();
-            parts[4] = registerNode((Expression) symbolIter.next());
+        if (!obj.getVariables().isEmpty()) {
+            beginClause(2);
 
-            // Add rest of the clauses
-            for(int i=5; symbolIter.hasNext(); i=i+2) {
-                parts[i] = ", "; //$NON-NLS-1$
-                parts[i+1] = registerNode((Expression) symbolIter.next());
+            // Columns clause
+            List vars = obj.getVariables();
+            if (vars != null) {
+                append("("); //$NON-NLS-1$
+                registerNodes(vars, 0);
+                append(")"); //$NON-NLS-1$
             }
-
-            replaceStringParts(parts);
-        } else {
-            // Shouldn't happen, but being tolerant
-            replaceStringParts(new Object[] { GROUP, SPACE, BY });
         }
-    }
-
-    public void visit(Insert obj) {
-        formatBasicInsert(obj);
-        
-        if ( obj.getQueryExpression() != null ) {
-            parts.add(registerNode(obj.getQueryExpression()));
+        beginClause(1);
+        if (obj.getQueryExpression() != null) {
+            visitNode(obj.getQueryExpression());
         } else if (obj.getTupleSource() != null) {
-        	parts.add(VALUES);
-            parts.add(" (...)"); //$NON-NLS-1$
-        } else {
-            parts.add(VALUES);
-            parts.add(" ("); //$NON-NLS-1$
-            Iterator valueIter = obj.getValues().iterator();
-            while(valueIter.hasNext()) {
-                Expression valObj = (Expression) valueIter.next();
-                parts.add(registerNode(valObj));
-                if(valueIter.hasNext()) {
-                    parts.add(", "); //$NON-NLS-1$
-                }
-            }
-            parts.add(")"); //$NON-NLS-1$
+            append(VALUES);
+            append(" (...)"); //$NON-NLS-1$
+        } else if (obj.getValues() != null) {
+            append(VALUES);
+            beginClause(2);
+            append("("); //$NON-NLS-1$
+            registerNodes(obj.getValues(), 0);
+            append(")"); //$NON-NLS-1$
         }
-            
-    	// Option clause
-		if(obj.getOption() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOption()));
-		}
+
+        // Option clause
+        if (obj.getOption() != null) {
+            beginClause(1);
+            visitNode(obj.getOption());
+        }
     }
 
-    public void visit(Create obj) {
-        parts.add(CREATE);
-        parts.add(SPACE);
-        parts.add(LOCAL);
-        parts.add(SPACE);
-        parts.add(TEMPORARY);
-        parts.add(SPACE);
-        parts.add(TABLE);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getTable()));
-        parts.add(SPACE);
+    public void visit( Create obj ) {
+        append(CREATE);
+        append(SPACE);
+        append(LOCAL);
+        append(SPACE);
+        append(TEMPORARY);
+        append(SPACE);
+        append(TABLE);
+        append(SPACE);
+        visitNode(obj.getTable());
+        append(SPACE);
 
         // Columns clause
         List<ElementSymbol> columns = obj.getColumns();
-        parts.add("("); //$NON-NLS-1$
+        append("("); //$NON-NLS-1$
         Iterator<ElementSymbol> iter = columns.iterator();
-        while(iter.hasNext()) {
+        while (iter.hasNext()) {
             ElementSymbol element = iter.next();
             outputShortName(element);
-            parts.add(SPACE);
-            parts.add(DataTypeManager.getDataTypeName(element.getType()));
-            if(iter.hasNext()) {
-                parts.add(", "); //$NON-NLS-1$
+            append(SPACE);
+            append(DataTypeManager.getDataTypeName(element.getType()));
+            if (iter.hasNext()) {
+                append(", "); //$NON-NLS-1$
             }
         }
         if (!obj.getPrimaryKey().isEmpty()) {
-        	parts.add(", "); //$NON-NLS-1$
-        	parts.add(PRIMARY);
-        	parts.add(" "); //$NON-NLS-1$
-        	parts.add(NonReserved.KEY);
-        	parts.add(Tokens.LPAREN);
-        	iter = obj.getPrimaryKey().iterator();
-            while(iter.hasNext()) {
-            	outputShortName(iter.next());
-            	if (iter.hasNext()) {
-            		parts.add(", "); //$NON-NLS-1$
-            	}
-            }
-        	parts.add(Tokens.RPAREN);
-        }
-        parts.add(")"); //$NON-NLS-1$
-    }
-    
-    public void visit(Drop obj) {
-        parts.add(DROP);
-        parts.add(SPACE);
-        parts.add(TABLE);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getTable()));
-    }
-    
-    private void formatBasicInsert(Insert obj) {
-        parts.add(INSERT);
-        parts.add(SPACE);
-        parts.add(INTO);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getGroup()));
-        parts.add(SPACE);
-        
-        if (!obj.getVariables().isEmpty()) {
-            
-            // Columns clause
-            List vars = obj.getVariables();
-            if(vars != null) {
-                parts.add("("); //$NON-NLS-1$
-                Iterator iter = vars.iterator();
-                while(iter.hasNext()) {
-                    ElementSymbol element = (ElementSymbol) iter.next();
-                    parts.add(registerNode(element));
-                    if(iter.hasNext()) {
-                        parts.add(", "); //$NON-NLS-1$
-                    }
+            append(", "); //$NON-NLS-1$
+            append(PRIMARY);
+            append(" "); //$NON-NLS-1$
+            append(NonReserved.KEY);
+            append(Tokens.LPAREN);
+            iter = obj.getPrimaryKey().iterator();
+            while (iter.hasNext()) {
+                outputShortName(iter.next());
+                if (iter.hasNext()) {
+                    append(", "); //$NON-NLS-1$
                 }
-                parts.add(") "); //$NON-NLS-1$
             }
+            append(Tokens.RPAREN);
         }
+        append(")"); //$NON-NLS-1$
     }
 
-    public void visit(IsNullCriteria obj) {
+    public void visit( Drop obj ) {
+        append(DROP);
+        append(SPACE);
+        append(TABLE);
+        append(SPACE);
+        visitNode(obj.getTable());
+    }
+
+    public void visit( IsNullCriteria obj ) {
         Expression expr = obj.getExpression();
-        Object exprPart = registerNode(expr);
-        parts.add(exprPart);
-        parts.add(SPACE);
-        parts.add(IS);
-        parts.add(SPACE);
+        visitNode(expr);
+        append(SPACE);
+        append(IS);
+        append(SPACE);
         if (obj.isNegated()) {
-            parts.add(NOT);
-            parts.add(SPACE);
+            append(NOT);
+            append(SPACE);
         }
-        parts.add(NULL);
+        append(NULL);
     }
 
-    public void visit(JoinPredicate obj) {
+    public void visit( JoinPredicate obj ) {
         addOptionComment(obj);
-        
-        if(obj.hasHint()) {
-            parts.add("(");//$NON-NLS-1$
+
+        if (obj.hasHint()) {
+            append("(");//$NON-NLS-1$
         }
 
         // left clause
         FromClause leftClause = obj.getLeftClause();
-		if(leftClause instanceof JoinPredicate && !((JoinPredicate)leftClause).hasHint()) {
-			parts.add("("); //$NON-NLS-1$
-			parts.add(registerNode(leftClause));
-			parts.add(")"); //$NON-NLS-1$
-		} else {
-			parts.add(registerNode(leftClause));
-		}
+        if (leftClause instanceof JoinPredicate && !((JoinPredicate)leftClause).hasHint()) {
+            append("("); //$NON-NLS-1$
+            visitNode(leftClause);
+            append(")"); //$NON-NLS-1$
+        } else {
+            visitNode(leftClause);
+        }
 
         // join type
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getJoinType()));
-        parts.add(SPACE);
+        append(SPACE);
+        visitNode(obj.getJoinType());
+        append(SPACE);
 
         // right clause
         FromClause rightClause = obj.getRightClause();
-		if(rightClause instanceof JoinPredicate && !((JoinPredicate)rightClause).hasHint()) {
-			parts.add("("); //$NON-NLS-1$
-			parts.add(registerNode(rightClause));
-			parts.add(")"); //$NON-NLS-1$
-		} else {
-			parts.add(registerNode(rightClause));
-		}
+        if (rightClause instanceof JoinPredicate && !((JoinPredicate)rightClause).hasHint()) {
+            append("("); //$NON-NLS-1$
+            visitNode(rightClause);
+            append(")"); //$NON-NLS-1$
+        } else {
+            visitNode(rightClause);
+        }
 
         // join criteria
         List joinCriteria = obj.getJoinCriteria();
-		if(joinCriteria != null && joinCriteria.size() > 0) {
-            parts.add(SPACE);
-			parts.add(ON);
-            parts.add(SPACE);
-			Iterator critIter = joinCriteria.iterator();
-			while(critIter.hasNext()) {
-				Criteria crit = (Criteria) critIter.next();
-                if(crit instanceof PredicateCriteria || crit instanceof AtomicCriteria) {
-    				parts.add(registerNode(crit));
+        if (joinCriteria != null && joinCriteria.size() > 0) {
+            append(SPACE);
+            append(ON);
+            append(SPACE);
+            Iterator critIter = joinCriteria.iterator();
+            while (critIter.hasNext()) {
+                Criteria crit = (Criteria)critIter.next();
+                if (crit instanceof PredicateCriteria || crit instanceof AtomicCriteria) {
+                    visitNode(crit);
                 } else {
-                    parts.add("("); //$NON-NLS-1$
-                    parts.add(registerNode(crit));
-                    parts.add(")"); //$NON-NLS-1$
+                    append("("); //$NON-NLS-1$
+                    visitNode(crit);
+                    append(")"); //$NON-NLS-1$
                 }
 
-				if(critIter.hasNext()) {
-					parts.add(SPACE);
-					parts.add(AND);
-					parts.add(SPACE);
-				}
-			}
-		}
+                if (critIter.hasNext()) {
+                    append(SPACE);
+                    append(AND);
+                    append(SPACE);
+                }
+            }
+        }
 
-        if(obj.hasHint()) {
-            parts.add(")"); //$NON-NLS-1$
+        if (obj.hasHint()) {
+            append(")"); //$NON-NLS-1$
         }
         addFromClasueDepOptions(obj);
     }
 
-    private void addFromClasueDepOptions(FromClause obj) {
+    private void addFromClasueDepOptions( FromClause obj ) {
         if (obj.isMakeDep()) {
-            parts.add(SPACE);
-            parts.add(Option.MAKEDEP);
+            append(SPACE);
+            append(Option.MAKEDEP);
         }
         if (obj.isMakeNotDep()) {
-            parts.add(SPACE);
-            parts.add(Option.MAKENOTDEP);
+            append(SPACE);
+            append(Option.MAKENOTDEP);
         }
     }
 
-    private void addOptionComment(FromClause obj) {
-    	if (obj.isOptional()) {
-	    	parts.add(BEGIN_HINT);
-	        parts.add(SPACE);
-	        parts.add(Option.OPTIONAL);
-	        parts.add(SPACE);
-	        parts.add(END_HINT);
-	        parts.add(SPACE);
-    	}
+    private void addOptionComment( FromClause obj ) {
+        if (obj.isOptional()) {
+            append(BEGIN_HINT);
+            append(SPACE);
+            append(Option.OPTIONAL);
+            append(SPACE);
+            append(END_HINT);
+            append(SPACE);
+        }
     }
 
-    public void visit(JoinType obj) {
-        Object[] parts = null;
-        if(obj.equals(JoinType.JOIN_INNER)) {
-            parts = new Object[] { INNER, SPACE, JOIN };
-        } else if(obj.equals(JoinType.JOIN_CROSS)) {
-            parts = new Object[] { CROSS, SPACE, JOIN };
-        } else if(obj.equals(JoinType.JOIN_LEFT_OUTER)) {
-            parts = new Object[] { LEFT, SPACE, OUTER, SPACE, JOIN };
-        } else if(obj.equals(JoinType.JOIN_RIGHT_OUTER)) {
-            parts = new Object[] { RIGHT, SPACE, OUTER, SPACE, JOIN };
-        } else if(obj.equals(JoinType.JOIN_FULL_OUTER)) {
-            parts = new Object[] { FULL, SPACE, OUTER, SPACE, JOIN };
-        } else if(obj.equals(JoinType.JOIN_UNION)) {
-            parts = new Object[] { UNION, SPACE, JOIN };
+    public void visit( JoinType obj ) {
+        String[] output = null;
+        if (obj.equals(JoinType.JOIN_INNER)) {
+            output = new String[] {INNER, SPACE, JOIN};
+        } else if (obj.equals(JoinType.JOIN_CROSS)) {
+            output = new String[] {CROSS, SPACE, JOIN};
+        } else if (obj.equals(JoinType.JOIN_LEFT_OUTER)) {
+            output = new String[] {LEFT, SPACE, OUTER, SPACE, JOIN};
+        } else if (obj.equals(JoinType.JOIN_RIGHT_OUTER)) {
+            output = new String[] {RIGHT, SPACE, OUTER, SPACE, JOIN};
+        } else if (obj.equals(JoinType.JOIN_FULL_OUTER)) {
+            output = new String[] {FULL, SPACE, OUTER, SPACE, JOIN};
+        } else if (obj.equals(JoinType.JOIN_UNION)) {
+            output = new String[] {UNION, SPACE, JOIN};
         } else if (obj.equals(JoinType.JOIN_SEMI)) {
-            parts = new Object[] { "SEMI", SPACE, JOIN }; //$NON-NLS-1$
+            output = new String[] {"SEMI", SPACE, JOIN}; //$NON-NLS-1$
         } else if (obj.equals(JoinType.JOIN_ANTI_SEMI)) {
-            parts = new Object[] { "ANTI SEMI", SPACE, JOIN }; //$NON-NLS-1$
+            output = new String[] {"ANTI SEMI", SPACE, JOIN}; //$NON-NLS-1$
+        } else {
+            throw new AssertionError();
         }
-
-        replaceStringParts(parts);
+        for (String part : output) {
+            append(part);
+        }
     }
 
-    public void visit(MatchCriteria obj) {
-        parts.add(registerNode(obj.getLeftExpression()));
+    public void visit( MatchCriteria obj ) {
+        visitNode(obj.getLeftExpression());
 
-        parts.add(SPACE);
+        append(SPACE);
         if (obj.isNegated()) {
-            parts.add(NOT);
-            parts.add(SPACE);
+            append(NOT);
+            append(SPACE);
         }
-        parts.add(LIKE);
-        parts.add(SPACE);
+        append(LIKE);
+        append(SPACE);
 
-        parts.add(registerNode(obj.getRightExpression()));
+        visitNode(obj.getRightExpression());
 
-        if(obj.getEscapeChar() != MatchCriteria.NULL_ESCAPE_CHAR) {
-            parts.add(SPACE);
-            parts.add(ESCAPE);
-            parts.add(" '"); //$NON-NLS-1$
-            parts.add("" + obj.getEscapeChar()); //$NON-NLS-1$
-            parts.add("'"); //$NON-NLS-1$
+        if (obj.getEscapeChar() != MatchCriteria.NULL_ESCAPE_CHAR) {
+            append(SPACE);
+            append(ESCAPE);
+            append(" '"); //$NON-NLS-1$
+            append("" + obj.getEscapeChar()); //$NON-NLS-1$
+            append("'"); //$NON-NLS-1$
         }
     }
 
-    public void visit(NotCriteria obj) {
-        parts.add(NOT);
-        parts.add(" ("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCriteria()));
-        parts.add(")"); //$NON-NLS-1$
+    public void visit( NotCriteria obj ) {
+        append(NOT);
+        append(" ("); //$NON-NLS-1$
+        visitNode(obj.getCriteria());
+        append(")"); //$NON-NLS-1$
     }
 
-    public void visit(Option obj) {
-        parts.add(OPTION);
+    public void visit( Option obj ) {
+        append(OPTION);
 
         Collection groups = obj.getDependentGroups();
-        if(groups != null && groups.size() > 0) {
-            parts.add(" "); //$NON-NLS-1$
-            parts.add(MAKEDEP);
-            parts.add(" "); //$NON-NLS-1$
+        if (groups != null && groups.size() > 0) {
+            append(" "); //$NON-NLS-1$
+            append(MAKEDEP);
+            append(" "); //$NON-NLS-1$
 
             Iterator iter = groups.iterator();
 
-            while(iter.hasNext()) {
+            while (iter.hasNext()) {
                 outputDisplayName((String)iter.next());
-                
+
                 if (iter.hasNext()) {
-                	parts.add(", ");
+                    append(", ");
                 }
             }
         }
-        
+
         groups = obj.getNotDependentGroups();
-        if(groups != null && groups.size() > 0) {
-            parts.add(" "); //$NON-NLS-1$
-            parts.add(MAKENOTDEP);
-            parts.add(" "); //$NON-NLS-1$
+        if (groups != null && groups.size() > 0) {
+            append(" "); //$NON-NLS-1$
+            append(MAKENOTDEP);
+            append(" "); //$NON-NLS-1$
 
             Iterator iter = groups.iterator();
 
-            while(iter.hasNext()) {
+            while (iter.hasNext()) {
                 outputDisplayName((String)iter.next());
-                
+
                 if (iter.hasNext()) {
-                	parts.add(", ");
+                    append(", ");
                 }
             }
         }
-        
+
         groups = obj.getNoCacheGroups();
-        if(groups != null && groups.size() > 0) {
-            parts.add(" "); //$NON-NLS-1$
-            parts.add(NOCACHE);
-            parts.add(" "); //$NON-NLS-1$
+        if (groups != null && groups.size() > 0) {
+            append(" "); //$NON-NLS-1$
+            append(NOCACHE);
+            append(" "); //$NON-NLS-1$
 
             Iterator iter = groups.iterator();
 
-            while(iter.hasNext()) {
+            while (iter.hasNext()) {
                 outputDisplayName((String)iter.next());
-                
+
                 if (iter.hasNext()) {
-                	parts.add(", ");
+                    append(", ");
                 }
             }
-        }else if(obj.isNoCache()){
-            parts.add(" "); //$NON-NLS-1$
-            parts.add(NOCACHE);
+        } else if (obj.isNoCache()) {
+            append(" "); //$NON-NLS-1$
+            append(NOCACHE);
         }
 
     }
 
-    public void visit(OrderBy obj) {
-        parts.add(ORDER);
-        parts.add(SPACE);
-        parts.add(BY);
-		parts.add(SPACE);
-		for (Iterator<OrderByItem> iterator = obj.getOrderByItems().iterator(); iterator.hasNext();) {
-			OrderByItem item = iterator.next();
-			parts.add(registerNode(item));
-			if (iterator.hasNext()) {
-				parts.add( ", " ); //$NON-NLS-1$				
-			}
-		}
+    public void visit( OrderBy obj ) {
+        append(ORDER);
+        append(SPACE);
+        append(BY);
+        append(SPACE);
+        for (Iterator<OrderByItem> iterator = obj.getOrderByItems().iterator(); iterator.hasNext();) {
+            OrderByItem item = iterator.next();
+            visitNode(item);
+            if (iterator.hasNext()) {
+                append(", "); //$NON-NLS-1$				
+            }
+        }
     }
-    
+
     @Override
-    public void visit(OrderByItem obj) {
-    	SingleElementSymbol ses = obj.getSymbol();
-	    if (ses instanceof AliasSymbol) {
-	    	AliasSymbol as = (AliasSymbol)ses;
-	    	outputDisplayName(as.getOutputName());
-	    } else {
-	    	parts.add(registerNode(ses));
-	    }
-        if(!obj.isAscending()) {
-            parts.add(SPACE);
-            parts.add(DESC);
+    public void visit( OrderByItem obj ) {
+        SingleElementSymbol ses = obj.getSymbol();
+        if (ses instanceof AliasSymbol) {
+            AliasSymbol as = (AliasSymbol)ses;
+            outputDisplayName(as.getOutputName());
+        } else {
+            visitNode(ses);
+        }
+        if (!obj.isAscending()) {
+            append(SPACE);
+            append(DESC);
         } // Don't print default "ASC"
         if (obj.getNullOrdering() != null) {
-        	parts.add(SPACE);
-        	parts.add(NonReserved.NULLS);
-        	parts.add(SPACE);
-        	parts.add(obj.getNullOrdering().name());
+            append(SPACE);
+            append(NonReserved.NULLS);
+            append(SPACE);
+            append(obj.getNullOrdering().name());
         }
     }
-    
-    public void visit(DynamicCommand obj) {
-        parts.add(EXECUTE);
-        parts.add(SPACE);
-        parts.add(STRING);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getSql()));
 
-        if(obj.isAsClauseSet()){
-            parts.add(SPACE);
-            parts.add(AS);
-            parts.add(SPACE);
+    public void visit( DynamicCommand obj ) {
+        append(EXECUTE);
+        append(SPACE);
+        append(STRING);
+        append(SPACE);
+        visitNode(obj.getSql());
+
+        if (obj.isAsClauseSet()) {
+            beginClause(1);
+            append(AS);
+            append(SPACE);
             for (int i = 0; i < obj.getAsColumns().size(); i++) {
                 ElementSymbol symbol = (ElementSymbol)obj.getAsColumns().get(i);
                 outputShortName(symbol);
-                parts.add(SPACE);
-                parts.add(DataTypeManager.getDataTypeName(symbol.getType()));
+                append(SPACE);
+                append(DataTypeManager.getDataTypeName(symbol.getType()));
                 if (i < obj.getAsColumns().size() - 1) {
-                    parts.add(", "); //$NON-NLS-1$
+                    append(", "); //$NON-NLS-1$
                 }
             }
         }
 
-        if(obj.getIntoGroup() != null){
-            parts.add(SPACE);
-            parts.add(INTO);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getIntoGroup()));
+        if (obj.getIntoGroup() != null) {
+            beginClause(1);
+            append(INTO);
+            append(SPACE);
+            visitNode(obj.getIntoGroup());
         }
 
-        if(obj.getUsing() != null && !obj.getUsing().isEmpty()) {
-            parts.add(SPACE);
-            parts.add(USING);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getUsing()));
+        if (obj.getUsing() != null && !obj.getUsing().isEmpty()) {
+            beginClause(1);
+            append(USING);
+            append(SPACE);
+            visitNode(obj.getUsing());
         }
 
         if (obj.getUpdatingModelCount() > 0) {
-            parts.add(SPACE);
-            parts.add(UPDATE);
-            parts.add(SPACE);
+            beginClause(1);
+            append(UPDATE);
+            append(SPACE);
             if (obj.getUpdatingModelCount() > 1) {
-                parts.add("*"); //$NON-NLS-1$
+                append("*"); //$NON-NLS-1$
             } else {
-                parts.add("1"); //$NON-NLS-1$
+                append("1"); //$NON-NLS-1$
             }
         }
     }
 
-    public void visit(SetClauseList obj) {
-    	for (Iterator<SetClause> iterator = obj.getClauses().iterator(); iterator.hasNext();) {
-			SetClause clause = iterator.next();
-			parts.add(registerNode(clause));
+    public void visit( SetClauseList obj ) {
+        for (Iterator<SetClause> iterator = obj.getClauses().iterator(); iterator.hasNext();) {
+            SetClause clause = iterator.next();
+            visitNode(clause);
             if (iterator.hasNext()) {
-                parts.add(", "); //$NON-NLS-1$
+                append(", "); //$NON-NLS-1$
             }
-		}
+        }
     }
-    
-    public void visit(SetClause obj) {
+
+    public void visit( SetClause obj ) {
         ElementSymbol symbol = obj.getSymbol();
         outputShortName(symbol);
-        parts.add(" = "); //$NON-NLS-1$
-        parts.add(registerNode(obj.getValue()));
+        append(" = "); //$NON-NLS-1$
+        visitNode(obj.getValue());
     }
 
-    public void visit(Query obj) {
-    	addCacheHint(obj.getCacheHint());
-        parts.add(registerNode(obj.getSelect()));
+    public void visit( Query obj ) {
+        addCacheHint(obj.getCacheHint());
+        visitNode(obj.getSelect());
 
-        if(obj.getInto() != null){
-            parts.add(SPACE);
-            parts.add(INTO);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getInto()));
+        if (obj.getInto() != null) {
+            beginClause(1);
+            visitNode(obj.getInto());
         }
 
-        if(obj.getFrom() != null){
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getFrom()));
+        if (obj.getFrom() != null) {
+            beginClause(1);
+            visitNode(obj.getFrom());
         }
 
         // Where clause
-        if(obj.getCriteria() != null) {
-            parts.add(SPACE);
-            parts.add(WHERE);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getCriteria()));
+        if (obj.getCriteria() != null) {
+            beginClause(1);
+            visitCriteria(WHERE, obj.getCriteria());
         }
 
-		// Group by clause
-        if(obj.getGroupBy() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getGroupBy()));
+        // Group by clause
+        if (obj.getGroupBy() != null) {
+            beginClause(1);
+            visitNode(obj.getGroupBy());
         }
 
-		// Having clause
-		if(obj.getHaving() != null) {
-            parts.add(SPACE);
-            parts.add(HAVING);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getHaving()));
-		}
+        // Having clause
+        if (obj.getHaving() != null) {
+            beginClause(1);
+            visitCriteria(HAVING, obj.getHaving());
+        }
 
-		// Order by clause
-		if(obj.getOrderBy() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOrderBy()));
-		}
-        
+        // Order by clause
+        if (obj.getOrderBy() != null) {
+            beginClause(1);
+            visitNode(obj.getOrderBy());
+        }
+
         if (obj.getLimit() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getLimit()));
+            beginClause(1);
+            visitNode(obj.getLimit());
         }
 
-		// Option clause
-		if(obj.getOption() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOption()));
-		}
+        // Option clause
+        if (obj.getOption() != null) {
+            beginClause(1);
+            visitNode(obj.getOption());
+        }
     }
 
-    public void visit(SearchedCaseExpression obj) {
-        parts.add(CASE);
+    protected void visitCriteria( String keyWord,
+                                  Criteria crit ) {
+        append(keyWord);
+        append(SPACE);
+        visitNode(crit);
+    }
+
+    public void visit( SearchedCaseExpression obj ) {
+        append(CASE);
         for (int i = 0; i < obj.getWhenCount(); i++) {
-            parts.add(SPACE);
-            parts.add(WHEN);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getWhenCriteria(i)));
-            parts.add(SPACE);
-            parts.add(THEN);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getThenExpression(i)));
+            append(SPACE);
+            append(WHEN);
+            append(SPACE);
+            visitNode(obj.getWhenCriteria(i));
+            append(SPACE);
+            append(THEN);
+            append(SPACE);
+            visitNode(obj.getThenExpression(i));
         }
-        parts.add(SPACE);
+        append(SPACE);
         if (obj.getElseExpression() != null) {
-            parts.add(ELSE);
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getElseExpression()));
-            parts.add(SPACE);
+            append(ELSE);
+            append(SPACE);
+            visitNode(obj.getElseExpression());
+            append(SPACE);
         }
-        parts.add(END);
+        append(END);
     }
 
-    public void visit(Select obj) {
-        parts.add(SELECT);
-        parts.add(SPACE);
+    public void visit( Select obj ) {
+        append(SELECT);
+        if (obj.isDistinct()) {
+            append(SPACE);
+            append(DISTINCT);
+        }
+        beginClause(2);
 
-		if(obj.isDistinct()) {
-			parts.add(DISTINCT);
-			parts.add(SPACE);
-		}
-
-	    Iterator iter = obj.getSymbols().iterator();
-        while( iter.hasNext() ) {
-			SelectSymbol symbol = (SelectSymbol) iter.next();
-			parts.add(registerNode(symbol));
-			if(iter.hasNext()) {
-				parts.add(", "); //$NON-NLS-1$
-			}
+        Iterator iter = obj.getSymbols().iterator();
+        while (iter.hasNext()) {
+            SelectSymbol symbol = (SelectSymbol)iter.next();
+            visitNode(symbol);
+            if (iter.hasNext()) {
+                append(", "); //$NON-NLS-1$
+            }
         }
     }
 
-    public void visit(SetCriteria obj) {
-		// variable
-		parts.add(registerNode(obj.getExpression()));
+    public void visit( SetCriteria obj ) {
+        // variable
+        visitNode(obj.getExpression());
 
-		// operator and beginning of list
-		parts.add(SPACE);
+        // operator and beginning of list
+        append(SPACE);
         if (obj.isNegated()) {
-            parts.add(NOT);
-            parts.add(SPACE);
+            append(NOT);
+            append(SPACE);
         }
-		parts.add(IN);
-		parts.add(" ("); //$NON-NLS-1$
+        append(IN);
+        append(" ("); //$NON-NLS-1$
 
-		// value list
-		Collection vals = obj.getValues();
-		int size = vals.size();
-		if(size == 1) {
-			Iterator iter = vals.iterator();
-			Expression expr = (Expression) iter.next();
-			parts.add( registerNode( expr ) );
-		} else if(size > 1) {
-			Iterator iter = vals.iterator();
-			Expression expr = (Expression) iter.next();
-			parts.add( registerNode( expr ) );
-			while(iter.hasNext()) {
-			    expr = (Expression) iter.next();
-				parts.add(", "); //$NON-NLS-1$
-				parts.add( registerNode( expr ) );
-			}
-		}
-		parts.add(")"); //$NON-NLS-1$
+        // value list
+        Collection vals = obj.getValues();
+        int size = vals.size();
+        if (size == 1) {
+            Iterator iter = vals.iterator();
+            Expression expr = (Expression)iter.next();
+            visitNode(expr);
+        } else if (size > 1) {
+            Iterator iter = vals.iterator();
+            Expression expr = (Expression)iter.next();
+            visitNode(expr);
+            while (iter.hasNext()) {
+                expr = (Expression)iter.next();
+                append(", "); //$NON-NLS-1$
+                visitNode(expr);
+            }
+        }
+        append(")"); //$NON-NLS-1$
     }
 
-    public void visit(SetQuery obj) {
-    	addCacheHint(obj.getCacheHint());
+    public void visit( SetQuery obj ) {
+        addCacheHint(obj.getCacheHint());
         QueryCommand query = obj.getLeftQuery();
-        if(query instanceof Query) {
-            parts.add(registerNode(query));
-        } else {
-            parts.add("("); //$NON-NLS-1$
-            parts.add(registerNode(query));
-            parts.add(")"); //$NON-NLS-1$
-        }
+        appendSetQuery(obj, query, false);
 
-        parts.add(SPACE);
-        parts.add(obj.getOperation());
-        parts.add(SPACE);
+        beginClause(0);
+        append(obj.getOperation());
 
-        if(obj.isAll()) {
-            parts.add(ALL);
-            parts.add(SPACE);
+        if (obj.isAll()) {
+            append(SPACE);
+            append(ALL);
         }
-
+        beginClause(0);
         query = obj.getRightQuery();
-        if(query instanceof Query) {
-            parts.add(registerNode(query));
-        } else {
-            parts.add("("); //$NON-NLS-1$
-            parts.add(registerNode(query));
-            parts.add(")"); //$NON-NLS-1$
+        appendSetQuery(obj, query, true);
+
+        if (obj.getOrderBy() != null) {
+            beginClause(0);
+            visitNode(obj.getOrderBy());
         }
 
-        if(obj.getOrderBy() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOrderBy()));
+        if (obj.getLimit() != null) {
+            beginClause(0);
+            visitNode(obj.getLimit());
         }
 
-        if(obj.getLimit() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getLimit()));
+        if (obj.getOption() != null) {
+            beginClause(0);
+            visitNode(obj.getOption());
         }
+    }
 
-        if(obj.getOption() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOption()));
+    protected void appendSetQuery( SetQuery parent,
+                                   QueryCommand obj,
+                                   boolean right ) {
+        if (right && obj instanceof SetQuery
+            && ((parent.isAll() && !((SetQuery)obj).isAll()) || parent.getOperation() != ((SetQuery)obj).getOperation())) {
+            append(Tokens.LPAREN);
+            visitNode(obj);
+            append(Tokens.RPAREN);
+        } else {
+            visitNode(obj);
         }
     }
 
-    public void visit(StoredProcedure obj) {
-    	addCacheHint(obj.getCacheHint());
-        //exec clause
-        parts.add(EXEC);
-		parts.add(SPACE);
-		parts.add(obj.getProcedureName());
-		parts.add("("); //$NON-NLS-1$
-		List params = obj.getInputParameters();
-        if(params != null) {
+    public void visit( StoredProcedure obj ) {
+        addCacheHint(obj.getCacheHint());
+        // exec clause
+        append(EXEC);
+        append(SPACE);
+        append(obj.getProcedureName());
+        append("("); //$NON-NLS-1$
+        List params = obj.getInputParameters();
+        if (params != null) {
             Iterator iter = params.iterator();
-            while(iter.hasNext()) {
-            	SPParameter param = (SPParameter) iter.next();
-                
-            	if (obj.displayNamedParameters()) {
-            	    parts.add(escapeSinglePart(ElementSymbol.getShortName(param.getParameterSymbol().getOutputName())));
-                    parts.add(" => "); //$NON-NLS-1$
+            while (iter.hasNext()) {
+                SPParameter param = (SPParameter)iter.next();
+
+                if (obj.displayNamedParameters()) {
+                    append(escapeSinglePart(ElementSymbol.getShortName(param.getParameterSymbol().getOutputName())));
+                    append(" => "); //$NON-NLS-1$
                 }
-                
-                if(param.getExpression() == null) {
-                    if(param.getName() != null) {
-                    	outputDisplayName(obj.getParamFullName(param));
+
+                if (param.getExpression() == null) {
+                    if (param.getName() != null) {
+                        outputDisplayName(obj.getParamFullName(param));
                     } else {
-                        parts.add("?"); //$NON-NLS-1$
+                        append("?"); //$NON-NLS-1$
                     }
                 } else {
-                	boolean addParens = !obj.displayNamedParameters() && param.getExpression() instanceof CompareCriteria;
-                	if (addParens) {
-                		parts.add(Tokens.LPAREN);
-                	}
-                    parts.add(registerNode(param.getExpression()));
+                    boolean addParens = !obj.displayNamedParameters() && param.getExpression() instanceof CompareCriteria;
                     if (addParens) {
-                		parts.add(Tokens.RPAREN);
-                	}
+                        append(Tokens.LPAREN);
+                    }
+                    visitNode(param.getExpression());
+                    if (addParens) {
+                        append(Tokens.RPAREN);
+                    }
                 }
-                if(iter.hasNext()) {
-                    parts.add(", "); //$NON-NLS-1$
+                if (iter.hasNext()) {
+                    append(", "); //$NON-NLS-1$
                 }
             }
         }
-        parts.add(")"); //$NON-NLS-1$
+        append(")"); //$NON-NLS-1$
 
         // Option clause
-		if(obj.getOption() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOption()));
-		}else{
-			parts.add(""); //$NON-NLS-1$
-		}
+        if (obj.getOption() != null) {
+            beginClause(1);
+            visitNode(obj.getOption());
+        }
     }
 
-	public void addCacheHint(CacheHint obj) {
-		if (obj == null) {
-			return;
-		}
-		parts.add(BEGIN_HINT);
-        parts.add(SPACE);
-        parts.add(CacheHint.CACHE);
+    public void addCacheHint( CacheHint obj ) {
+        if (obj == null) {
+            return;
+        }
+        append(BEGIN_HINT);
+        append(SPACE);
+        append(CacheHint.CACHE);
         boolean addParens = false;
         if (obj.getPrefersMemory()) {
-        	parts.add(Tokens.LPAREN);
-        	addParens = true;
-        	parts.add(CacheHint.PREF_MEM);
+            append(Tokens.LPAREN);
+            addParens = true;
+            append(CacheHint.PREF_MEM);
         }
         if (obj.getTtl() != null) {
-        	if (!addParens) {
-        		parts.add(Tokens.LPAREN);
-        		addParens = true;
-        	} else {
-		        parts.add(SPACE);
-        	}
-        	parts.add(CacheHint.TTL);
-        	parts.add(obj.getTtl());
+            if (!addParens) {
+                append(Tokens.LPAREN);
+                addParens = true;
+            } else {
+                append(SPACE);
+            }
+            append(CacheHint.TTL);
+            append(obj.getTtl());
         }
         if (obj.isUpdatable()) {
-        	if (!addParens) {
-        		parts.add(Tokens.LPAREN);
-        		addParens = true;
-        	} else {
-		        parts.add(SPACE);
-        	}
-        	parts.add(CacheHint.UPDATABLE);
+            if (!addParens) {
+                append(Tokens.LPAREN);
+                addParens = true;
+            } else {
+                append(SPACE);
+            }
+            append(CacheHint.UPDATABLE);
         }
         if (addParens) {
-	        parts.add(Tokens.RPAREN);
+            append(Tokens.RPAREN);
         }
-        parts.add(SPACE);
-        parts.add(END_HINT);
-        parts.add(SPACE);
-	}
+        append(SPACE);
+        append(END_HINT);
+        beginClause(0);
+    }
 
-    public void visit(SubqueryFromClause obj) {
+    public void visit( SubqueryFromClause obj ) {
         addOptionComment(obj);
         if (obj.isTable()) {
-        	parts.add(TABLE);
+            append(TABLE);
         }
-        parts.add("(");//$NON-NLS-1$
-        parts.add(registerNode(obj.getCommand()));
-        parts.add(")");//$NON-NLS-1$
-        parts.add(" AS ");//$NON-NLS-1$
-        parts.add(obj.getOutputName());
+        append("(");//$NON-NLS-1$
+        visitNode(obj.getCommand());
+        append(")");//$NON-NLS-1$
+        append(" AS ");//$NON-NLS-1$
+        append(obj.getOutputName());
         addFromClasueDepOptions(obj);
     }
 
-    public void visit(SubquerySetCriteria obj) {
+    public void visit( SubquerySetCriteria obj ) {
         // variable
-        parts.add(registerNode(obj.getExpression()));
+        visitNode(obj.getExpression());
 
         // operator and beginning of list
-        parts.add(SPACE);
+        append(SPACE);
         if (obj.isNegated()) {
-            parts.add(NOT);
-            parts.add(SPACE);
+            append(NOT);
+            append(SPACE);
         }
-        parts.add(IN);
-        parts.add(" ("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCommand()));
-        parts.add(")"); //$NON-NLS-1$
+        append(IN);
+        append(" ("); //$NON-NLS-1$
+        visitNode(obj.getCommand());
+        append(")"); //$NON-NLS-1$
     }
 
-
-    public void visit(UnaryFromClause obj) {
+    public void visit( UnaryFromClause obj ) {
         addOptionComment(obj);
-        parts.add(registerNode(obj.getGroup()));
+        visitNode(obj.getGroup());
         addFromClasueDepOptions(obj);
     }
 
-    public void visit(Update obj) {
+    public void visit( Update obj ) {
         // Update clause
-        parts.add(UPDATE);
-		parts.add(SPACE);
-        parts.add(registerNode(obj.getGroup()));
-		parts.add(SPACE);
-
+        append(UPDATE);
+        append(SPACE);
+        visitNode(obj.getGroup());
+        beginClause(1);
         // Set clause
-        parts.add(SET);
-        parts.add(SPACE);
+        append(SET);
+        beginClause(2);
+        visitNode(obj.getChangeList());
 
-        parts.add(registerNode(obj.getChangeList()));
-        
-		// Where clause
-		if(obj.getCriteria() != null) {
-			parts.add(SPACE);
-			parts.add(WHERE);
-			parts.add(SPACE);
-			parts.add(registerNode(obj.getCriteria()));
-		}
+        // Where clause
+        if (obj.getCriteria() != null) {
+            beginClause(1);
+            visitCriteria(WHERE, obj.getCriteria());
+        }
 
-		// Option clause
-		if(obj.getOption() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOption()));
-		}
+        // Option clause
+        if (obj.getOption() != null) {
+            beginClause(1);
+            visitNode(obj.getOption());
+        }
     }
 
-    public void visit(Into obj) {
-        parts.add(registerNode(obj.getGroup()));
+    public void visit( Into obj ) {
+        append(INTO);
+        append(SPACE);
+        visitNode(obj.getGroup());
     }
 
     // ############ Visitor methods for symbol objects ####################
 
-    public void visit(AggregateSymbol obj) {
-        parts.add(obj.getAggregateFunction().name());
-		parts.add("("); //$NON-NLS-1$
+    public void visit( AggregateSymbol obj ) {
+        append(obj.getAggregateFunction().name());
+        append("("); //$NON-NLS-1$
 
-		if(obj.isDistinct()) {
-			parts.add(DISTINCT);
-			parts.add(" "); //$NON-NLS-1$
-		}
+        if (obj.isDistinct()) {
+            append(DISTINCT);
+            append(" "); //$NON-NLS-1$
+        }
 
-		if(obj.getExpression() == null) {
-			parts.add(Tokens.ALL_COLS);
-		} else {
-			parts.add(registerNode(obj.getExpression()));
-		}
-		
-		if (obj.getOrderBy() != null) {
-			parts.add(SPACE);
-			parts.add(registerNode(obj.getOrderBy()));
-		}
-		parts.add(")"); //$NON-NLS-1$
+        if (obj.getExpression() == null) {
+            append(Tokens.ALL_COLS);
+        } else {
+            visitNode(obj.getExpression());
+        }
+
+        if (obj.getOrderBy() != null) {
+            append(SPACE);
+            visitNode(obj.getOrderBy());
+        }
+        append(")"); //$NON-NLS-1$
     }
 
-    public void visit(AliasSymbol obj) {
-        parts.add(registerNode(obj.getSymbol()));
-        parts.add(SPACE);
-        parts.add(AS);
-        parts.add(SPACE);
-        parts.add(escapeSinglePart(obj.getOutputName()));
+    public void visit( AliasSymbol obj ) {
+        visitNode(obj.getSymbol());
+        append(SPACE);
+        append(AS);
+        append(SPACE);
+        append(escapeSinglePart(obj.getOutputName()));
     }
 
-    public void visit(AllInGroupSymbol obj) {
-        parts.add(obj.getName());
+    public void visit( AllInGroupSymbol obj ) {
+        append(obj.getName());
     }
 
-    public void visit(AllSymbol obj) {
-        parts.add(obj.getName());
+    public void visit( AllSymbol obj ) {
+        append(obj.getName());
     }
 
-    public void visit(Constant obj) {
+    public void visit( Constant obj ) {
         Class<?> type = obj.getType();
-        Object[] constantParts = null;
+        String[] constantParts = null;
         if (obj.isMultiValued()) {
-        	constantParts = new Object[] {"?"}; //$NON-NLS-1$
-        } else if(obj.isNull()) {
-        	if(type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) {
-    			constantParts = new Object[] {UNKNOWN};
-        	} else {
-    			constantParts = new Object[] {"null"}; //$NON-NLS-1$
-        	}
-		} else {
-            if(Number.class.isAssignableFrom(type)) {
-                constantParts = new Object[] { obj.getValue().toString() };
-            } else if(type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) {
-                constantParts = new Object[] { obj.getValue().equals(Boolean.TRUE) ? TRUE : FALSE}; 
-		    } else if(type.equals(DataTypeManager.DefaultDataClasses.TIMESTAMP)) {
-                constantParts = new Object[] { "{ts'", obj.getValue().toString(), "'}" }; //$NON-NLS-1$ //$NON-NLS-2$
-            } else if(type.equals(DataTypeManager.DefaultDataClasses.TIME)) {
-                constantParts = new Object[] { "{t'", obj.getValue().toString(), "'}" }; //$NON-NLS-1$ //$NON-NLS-2$
-            } else if(type.equals(DataTypeManager.DefaultDataClasses.DATE)) {
-                constantParts = new Object[] { "{d'", obj.getValue().toString(), "'}" }; //$NON-NLS-1$ //$NON-NLS-2$
-            } 
+            constantParts = new String[] {"?"}; //$NON-NLS-1$
+        } else if (obj.isNull()) {
+            if (type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) {
+                constantParts = new String[] {UNKNOWN};
+            } else {
+                constantParts = new String[] {"null"}; //$NON-NLS-1$
+            }
+        } else {
+            if (Number.class.isAssignableFrom(type)) {
+                constantParts = new String[] {obj.getValue().toString()};
+            } else if (type.equals(DataTypeManager.DefaultDataClasses.BOOLEAN)) {
+                constantParts = new String[] {obj.getValue().equals(Boolean.TRUE) ? TRUE : FALSE};
+            } else if (type.equals(DataTypeManager.DefaultDataClasses.TIMESTAMP)) {
+                constantParts = new String[] {"{ts'", obj.getValue().toString(), "'}"}; //$NON-NLS-1$ //$NON-NLS-2$
+            } else if (type.equals(DataTypeManager.DefaultDataClasses.TIME)) {
+                constantParts = new String[] {"{t'", obj.getValue().toString(), "'}"}; //$NON-NLS-1$ //$NON-NLS-2$
+            } else if (type.equals(DataTypeManager.DefaultDataClasses.DATE)) {
+                constantParts = new String[] {"{d'", obj.getValue().toString(), "'}"}; //$NON-NLS-1$ //$NON-NLS-2$
+            }
             if (constantParts == null) {
-            	String strValue = obj.getValue().toString();
-		        strValue = escapeStringValue(strValue, "'"); //$NON-NLS-1$
-			    constantParts = new Object[] { "'", strValue, "'" }; //$NON-NLS-1$ //$NON-NLS-2$
+                String strValue = obj.getValue().toString();
+                strValue = escapeStringValue(strValue, "'"); //$NON-NLS-1$
+                constantParts = new String[] {"'", strValue, "'"}; //$NON-NLS-1$ //$NON-NLS-2$
             }
         }
 
-        replaceStringParts(constantParts);
+        for (String string : constantParts) {
+            append(string);
+        }
     }
 
- 	/**
- 	 * Take a string literal and escape it as necessary.  By default, this converts ' to ''.
- 	 * @param str String literal value (unquoted), never null
- 	 * @return Escaped string literal value
- 	 */
-    static String escapeStringValue(String str, String tick) {
+    /**
+     * Take a string literal and escape it as necessary. By default, this converts ' to ''.
+     * 
+     * @param str String literal value (unquoted), never null
+     * @return Escaped string literal value
+     */
+    static String escapeStringValue( String str,
+                                     String tick ) {
         return StringUtil.replaceAll(str, tick, tick + tick);
     }
 
-    public void visit(ElementSymbol obj) {
+    public void visit( ElementSymbol obj ) {
         if (obj.getDisplayMode().equals(ElementSymbol.DisplayMode.SHORT_OUTPUT_NAME)) {
             outputShortName(obj);
             return;
@@ -1241,704 +1151,706 @@
         }
         outputDisplayName(name);
     }
-    
-    private void outputShortName(ElementSymbol obj) {
-    	outputDisplayName(SingleElementSymbol.getShortName(obj.getOutputName()));
+
+    private void outputShortName( ElementSymbol obj ) {
+        outputDisplayName(SingleElementSymbol.getShortName(obj.getOutputName()));
     }
 
-    private void outputDisplayName(String name) {
+    private void outputDisplayName( String name ) {
         String[] pathParts = name.split("\\."); //$NON-NLS-1$
         for (int i = 0; i < pathParts.length; i++) {
             if (i > 0) {
-                parts.add(ElementSymbol.SEPARATOR);
+                append(ElementSymbol.SEPARATOR);
             }
-            parts.add(escapeSinglePart(pathParts[i]));
+            append(escapeSinglePart(pathParts[i]));
         }
     }
 
-    public void visit(ExpressionSymbol obj) {
-        parts.add(registerNode(obj.getExpression()));
+    public void visit( ExpressionSymbol obj ) {
+        visitNode(obj.getExpression());
     }
 
-    public void visit(Function obj) {
+    public void visit( Function obj ) {
         String name = obj.getName();
         Expression[] args = obj.getArgs();
-		if(obj.isImplicit()) {
-			// Hide this function, which is implicit
-            parts.add(registerNode(args[0]));
+        if (obj.isImplicit()) {
+            // Hide this function, which is implicit
+            visitNode(args[0]);
 
-		} else if(name.equalsIgnoreCase(CONVERT) || name.equalsIgnoreCase(CAST)) {
-			parts.add(name);
-			parts.add("("); //$NON-NLS-1$
+        } else if (name.equalsIgnoreCase(CONVERT) || name.equalsIgnoreCase(CAST)) {
+            append(name);
+            append("("); //$NON-NLS-1$
 
-			if(args != null && args.length > 0) {
-				parts.add(registerNode(args[0]));
+            if (args != null && args.length > 0) {
+                visitNode(args[0]);
 
-				if(name.equalsIgnoreCase(CONVERT)) {
-					parts.add(", "); //$NON-NLS-1$
-				} else {
-					parts.add(" "); //$NON-NLS-1$
-					parts.add(AS);
-					parts.add(" "); //$NON-NLS-1$
-				}
+                if (name.equalsIgnoreCase(CONVERT)) {
+                    append(", "); //$NON-NLS-1$
+                } else {
+                    append(" "); //$NON-NLS-1$
+                    append(AS);
+                    append(" "); //$NON-NLS-1$
+                }
 
-				if(args.length < 2 || args[1] == null || !(args[1] instanceof Constant)) {
-				    parts.add(UNDEFINED);
-				} else {
-					parts.add(((Constant)args[1]).getValue());
-				}
-			}
-			parts.add(")"); //$NON-NLS-1$
+                if (args.length < 2 || args[1] == null || !(args[1] instanceof Constant)) {
+                    append(UNDEFINED);
+                } else {
+                    append(((Constant)args[1]).getValue());
+                }
+            }
+            append(")"); //$NON-NLS-1$
 
-		} else if(name.equals("+") || name.equals("-") || name.equals("*") || name.equals("/") || name.equals("||")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
-			parts.add("("); //$NON-NLS-1$
+        } else if (name.equals("+") || name.equals("-") || name.equals("*") || name.equals("/") || name.equals("||")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+            append("("); //$NON-NLS-1$
 
-			if(args != null) {
-				for(int i=0; i<args.length; i++) {
-					parts.add(registerNode(args[i]));
-					if(i < (args.length-1)) {
-						parts.add(SPACE);
-						parts.add(name);
-						parts.add(SPACE);
-					}
-				}
-			}
-			parts.add(")"); //$NON-NLS-1$
+            if (args != null) {
+                for (int i = 0; i < args.length; i++) {
+                    visitNode(args[i]);
+                    if (i < (args.length - 1)) {
+                        append(SPACE);
+                        append(name);
+                        append(SPACE);
+                    }
+                }
+            }
+            append(")"); //$NON-NLS-1$
 
-        } else if(name.equalsIgnoreCase(NonReserved.TIMESTAMPADD) || name.equalsIgnoreCase(NonReserved.TIMESTAMPDIFF)) {
-            parts.add(name);
-            parts.add("("); //$NON-NLS-1$
+        } else if (name.equalsIgnoreCase(NonReserved.TIMESTAMPADD) || name.equalsIgnoreCase(NonReserved.TIMESTAMPDIFF)) {
+            append(name);
+            append("("); //$NON-NLS-1$
 
-            if(args != null && args.length > 0) {
-                parts.add(((Constant)args[0]).getValue());
+            if (args != null && args.length > 0) {
+                append(((Constant)args[0]).getValue());
                 registerNodes(args, 1);
             }
-            parts.add(")"); //$NON-NLS-1$
+            append(")"); //$NON-NLS-1$
 
-		} else if (name.equalsIgnoreCase(SourceSystemFunctions.XMLPI)){
-			parts.add(name);
-			parts.add("(NAME "); //$NON-NLS-1$
-			outputDisplayName((String)((Constant)args[0]).getValue());
-			registerNodes(args, 1);
-			parts.add(")"); //$NON-NLS-1$
-		} else {
-			parts.add(name);
-			parts.add("("); //$NON-NLS-1$
-			registerNodes(args, 0);
-			parts.add(")"); //$NON-NLS-1$
-		}
+        } else if (name.equalsIgnoreCase(SourceSystemFunctions.XMLPI)) {
+            append(name);
+            append("(NAME "); //$NON-NLS-1$
+            outputDisplayName((String)((Constant)args[0]).getValue());
+            registerNodes(args, 1);
+            append(")"); //$NON-NLS-1$
+        } else {
+            append(name);
+            append("("); //$NON-NLS-1$
+            registerNodes(args, 0);
+            append(")"); //$NON-NLS-1$
+        }
     }
-    
-    private void registerNodes(LanguageObject[] objects, int begin) {
-    	registerNodes(Arrays.asList(objects), begin);
+
+    private void registerNodes( LanguageObject[] objects,
+                                int begin ) {
+        registerNodes(Arrays.asList(objects), begin);
     }
-    
-    private void registerNodes(List<? extends LanguageObject> objects, int begin) {
-    	for (int i = begin; i < objects.size(); i++) {
-    		if (i > 0) {
-    			parts.add(", "); //$NON-NLS-1$
-    		}
-			parts.add(registerNode(objects.get(i)));
-		}
+
+    private void registerNodes( List<? extends LanguageObject> objects,
+                                int begin ) {
+        for (int i = begin; i < objects.size(); i++) {
+            if (i > 0) {
+                append(", "); //$NON-NLS-1$
+            }
+            visitNode(objects.get(i));
+        }
     }
 
-    public void visit(GroupSymbol obj) {
+    public void visit( GroupSymbol obj ) {
         String alias = null;
         String fullGroup = obj.getOutputName();
-        if(obj.getOutputDefinition() != null) {
+        if (obj.getOutputDefinition() != null) {
             alias = obj.getOutputName();
             fullGroup = obj.getOutputDefinition();
         }
-        
+
         outputDisplayName(fullGroup);
 
-        if(alias != null) {
-            parts.add(SPACE);
-            parts.add(AS);
-            parts.add(SPACE);
-            parts.add(escapeSinglePart(alias));
+        if (alias != null) {
+            append(SPACE);
+            append(AS);
+            append(SPACE);
+            append(escapeSinglePart(alias));
         }
     }
 
-    public void visit(Reference obj) {
+    public void visit( Reference obj ) {
         if (!obj.isPositional() && obj.getExpression() != null) {
-            replaceStringParts(new Object[] { obj.getExpression().toString() });
+            visitNode(obj.getExpression());
         } else {
-            replaceStringParts(new Object[] { "?" }); //$NON-NLS-1$
+            append("?"); //$NON-NLS-1$
         }
     }
 
     // ############ Visitor methods for storedprocedure language objects ####################
 
-    public void visit(Block obj) {
-    	List statements = obj.getStatements();
-    	if(statements.size() == 1) {
-    		replaceStringParts(new Object[] { BEGIN, "\n", //$NON-NLS-1$
-			registerNode((Statement)obj.getStatements().get(0)), "\n", END}); //$NON-NLS-1$
-    	} else if(statements.size() > 1) {
-	        List parts = new ArrayList();
-            // Add first clause
-            parts.add(BEGIN);
-            parts.add("\n"); //$NON-NLS-1$
-            Iterator stmtIter = statements.iterator();
-            while(stmtIter.hasNext()) {
-				// Add each statement
-	            parts.add(registerNode((Statement) stmtIter.next()));
-                parts.add("\n"); //$NON-NLS-1$
-            }
-            parts.add(END);
-            replaceStringParts(parts.toArray());
-        } else {
-            // Shouldn't happen, but being tolerant
-            replaceStringParts(new Object[] { BEGIN, "\n", //$NON-NLS-1$
-            							END });
+    public void visit( Block obj ) {
+        List statements = obj.getStatements();
+        // Add first clause
+        append(BEGIN);
+        append("\n"); //$NON-NLS-1$
+        Iterator stmtIter = statements.iterator();
+        while (stmtIter.hasNext()) {
+            // Add each statement
+            addTabs(1);
+            visitNode((Statement)stmtIter.next());
+            append("\n"); //$NON-NLS-1$
         }
+        addTabs(0);
+        append(END);
     }
 
-    public void visit(CommandStatement obj) {
-        parts.add(registerNode(obj.getCommand()));
-		parts.add(";"); //$NON-NLS-1$
+    protected void addTabs( int level ) {
     }
 
-    public void visit(CreateUpdateProcedureCommand obj) {
-        parts.add(CREATE);
-        parts.add(SPACE);
-        if(!obj.isUpdateProcedure()){
-            parts.add(VIRTUAL);
-            parts.add(SPACE);
+    public void visit( CommandStatement obj ) {
+        visitNode(obj.getCommand());
+        append(";"); //$NON-NLS-1$
+    }
+
+    public void visit( CreateUpdateProcedureCommand obj ) {
+        append(CREATE);
+        append(SPACE);
+        if (!obj.isUpdateProcedure()) {
+            append(VIRTUAL);
+            append(SPACE);
         }
-        parts.add(PROCEDURE);
-        parts.add("\n"); //$NON-NLS-1$
-        parts.add(registerNode(obj.getBlock()));
+        append(PROCEDURE);
+        append("\n"); //$NON-NLS-1$
+        addTabs(0);
+        visitNode(obj.getBlock());
     }
 
-    public void visit(DeclareStatement obj) {
-		parts.add(DECLARE);
-		parts.add(SPACE);
-        parts.add(obj.getVariableType());
-        parts.add(SPACE);
+    public void visit( DeclareStatement obj ) {
+        append(DECLARE);
+        append(SPACE);
+        append(obj.getVariableType());
+        append(SPACE);
         createAssignment(obj);
     }
 
-    /** 
+    /**
      * @param obj
      * @param parts
      */
-    private void createAssignment(AssignmentStatement obj) {
-        parts.add(registerNode(obj.getVariable()));
-        if (obj.getValue() != null) {
-            parts.add(" = "); //$NON-NLS-1$
+    private void createAssignment( AssignmentStatement obj ) {
+        visitNode(obj.getVariable());
+        if (obj.getExpression() != null) {
+            append(" = "); //$NON-NLS-1$
             addStatementArgument(obj.getExpression());
         }
-		parts.add(";"); //$NON-NLS-1$
+        append(";"); //$NON-NLS-1$
     }
 
-	private void addStatementArgument(Expression expr) {
-		if (expr instanceof ScalarSubquery) {
-			parts.add(registerNode(((ScalarSubquery)expr).getCommand()));
-		} else {
-			parts.add(registerNode(expr));
-		}
-	}
+    private void addStatementArgument( Expression expr ) {
+        if (expr instanceof ScalarSubquery) {
+            visitNode(((ScalarSubquery)expr).getCommand());
+        } else {
+            visitNode(expr);
+        }
+    }
 
-    public void visit(IfStatement obj) {
-        parts.add(IF);
-        parts.add("("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCondition()));
-        parts.add(")\n"); //$NON-NLS-1$
-        parts.add(registerNode(obj.getIfBlock()));
-        if(obj.hasElseBlock()) {
-        	parts.add("\n"); //$NON-NLS-1$
-	        parts.add(ELSE);
-	        parts.add("\n"); //$NON-NLS-1$
-	        parts.add(registerNode(obj.getElseBlock()));
+    public void visit( IfStatement obj ) {
+        append(IF);
+        append("("); //$NON-NLS-1$
+        visitNode(obj.getCondition());
+        append(")\n"); //$NON-NLS-1$
+        addTabs(0);
+        visitNode(obj.getIfBlock());
+        if (obj.hasElseBlock()) {
+            append("\n"); //$NON-NLS-1$
+            addTabs(0);
+            append(ELSE);
+            append("\n"); //$NON-NLS-1$
+            addTabs(0);
+            visitNode(obj.getElseBlock());
         }
     }
 
-    public void visit(AssignmentStatement obj) {
+    public void visit( AssignmentStatement obj ) {
         createAssignment(obj);
     }
 
-    public void visit(HasCriteria obj) {
-        parts.add( HAS);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getSelector()));
+    public void visit( HasCriteria obj ) {
+        append(HAS);
+        append(SPACE);
+        visitNode(obj.getSelector());
     }
 
-    public void visit(TranslateCriteria obj) {
-        parts.add(TRANSLATE);
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getSelector()));
+    public void visit( TranslateCriteria obj ) {
+        append(TRANSLATE);
+        append(SPACE);
+        visitNode(obj.getSelector());
 
-        if(obj.hasTranslations()) {
-	        parts.add(SPACE);
-	        parts.add(WITH);
-	        parts.add(SPACE);
-        	parts.add("("); //$NON-NLS-1$
-	        Iterator critIter = obj.getTranslations().iterator();
+        if (obj.hasTranslations()) {
+            append(SPACE);
+            append(WITH);
+            append(SPACE);
+            append("("); //$NON-NLS-1$
+            Iterator critIter = obj.getTranslations().iterator();
 
-	        while(critIter.hasNext()) {
-				parts.add(registerNode((Criteria)critIter.next()));
-				if(critIter.hasNext()) {
-					parts.add(", "); //$NON-NLS-1$
-				}
-				if(!critIter.hasNext()) {
-		        	parts.add(")"); //$NON-NLS-1$
-				}
-	        }
+            while (critIter.hasNext()) {
+                visitNode((Criteria)critIter.next());
+                if (critIter.hasNext()) {
+                    append(", "); //$NON-NLS-1$
+                }
+                if (!critIter.hasNext()) {
+                    append(")"); //$NON-NLS-1$
+                }
+            }
         }
     }
 
-    public void visit(CriteriaSelector obj) {
+    public void visit( CriteriaSelector obj ) {
         int selectorType = obj.getSelectorType();
 
-        switch(selectorType) {
-        	case CriteriaSelector.COMPARE_EQ:
-        		parts.add("= "); //$NON-NLS-1$
-        		break;
-        	case CriteriaSelector.COMPARE_GE:
-        		parts.add(">= "); //$NON-NLS-1$
-        		break;
-        	case CriteriaSelector.COMPARE_GT:
-        		parts.add("> "); //$NON-NLS-1$
-        		break;
-        	case CriteriaSelector.COMPARE_LE:
-        		parts.add("<= "); //$NON-NLS-1$
-        		break;
-        	case CriteriaSelector.COMPARE_LT:
-        		parts.add("< "); //$NON-NLS-1$
-        		break;
-        	case CriteriaSelector.COMPARE_NE:
-        		parts.add("<> "); //$NON-NLS-1$
-        		break;
-        	case CriteriaSelector.IN:
-        		parts.add(IN);
-        		parts.add(SPACE);
-        		break;
-        	case CriteriaSelector.IS_NULL:
-        		parts.add(IS);
-        		parts.add(SPACE);
-        		parts.add(NULL);
-        		parts.add(SPACE);
-        		break;
+        switch (selectorType) {
+            case CriteriaSelector.COMPARE_EQ:
+                append("= "); //$NON-NLS-1$
+                break;
+            case CriteriaSelector.COMPARE_GE:
+                append(">= "); //$NON-NLS-1$
+                break;
+            case CriteriaSelector.COMPARE_GT:
+                append("> "); //$NON-NLS-1$
+                break;
+            case CriteriaSelector.COMPARE_LE:
+                append("<= "); //$NON-NLS-1$
+                break;
+            case CriteriaSelector.COMPARE_LT:
+                append("< "); //$NON-NLS-1$
+                break;
+            case CriteriaSelector.COMPARE_NE:
+                append("<> "); //$NON-NLS-1$
+                break;
+            case CriteriaSelector.IN:
+                append(IN);
+                append(SPACE);
+                break;
+            case CriteriaSelector.IS_NULL:
+                append(IS);
+                append(SPACE);
+                append(NULL);
+                append(SPACE);
+                break;
             case CriteriaSelector.LIKE:
-                parts.add(LIKE);
-                parts.add(SPACE);
+                append(LIKE);
+                append(SPACE);
                 break;
             case CriteriaSelector.BETWEEN:
-                parts.add(BETWEEN);
-                parts.add(SPACE);
+                append(BETWEEN);
+                append(SPACE);
                 break;
         }
 
-        parts.add(CRITERIA);
-		if(obj.hasElements()) {
-	        parts.add(SPACE);
-	        parts.add(ON);
-	        parts.add(SPACE);
-	        parts.add("("); //$NON-NLS-1$
+        append(CRITERIA);
+        if (obj.hasElements()) {
+            append(SPACE);
+            append(ON);
+            append(SPACE);
+            append("("); //$NON-NLS-1$
 
-	        Iterator elmtIter = obj.getElements().iterator();
-	        while(elmtIter.hasNext()) {
-				parts.add(registerNode((ElementSymbol)elmtIter.next()));
-				if(elmtIter.hasNext()) {
-					parts.add(", "); //$NON-NLS-1$
-				}
-	        }
-	        parts.add(")"); //$NON-NLS-1$
-		}
+            Iterator elmtIter = obj.getElements().iterator();
+            while (elmtIter.hasNext()) {
+                visitNode((ElementSymbol)elmtIter.next());
+                if (elmtIter.hasNext()) {
+                    append(", "); //$NON-NLS-1$
+                }
+            }
+            append(")"); //$NON-NLS-1$
+        }
     }
 
-    public void visit(RaiseErrorStatement obj) {
-        parts.add(ERROR);
-        parts.add(SPACE);
+    public void visit( RaiseErrorStatement obj ) {
+        append(ERROR);
+        append(SPACE);
         addStatementArgument(obj.getExpression());
-        parts.add(";"); //$NON-NLS-1$
+        append(";"); //$NON-NLS-1$
     }
 
-    public void visit(BreakStatement obj) {
-        parts.add(BREAK);
-        parts.add(";"); //$NON-NLS-1$
+    public void visit( BreakStatement obj ) {
+        append(BREAK);
+        append(";"); //$NON-NLS-1$
     }
 
-    public void visit(ContinueStatement obj) {
-        parts.add(CONTINUE);
-        parts.add(";"); //$NON-NLS-1$
+    public void visit( ContinueStatement obj ) {
+        append(CONTINUE);
+        append(";"); //$NON-NLS-1$
     }
 
-    public void visit(LoopStatement obj) {
-        parts.add(LOOP);
-        parts.add(" "); //$NON-NLS-1$
-        parts.add(ON);
-        parts.add(" ("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCommand()));
-        parts.add(") "); //$NON-NLS-1$
-        parts.add(AS);
-        parts.add(" "); //$NON-NLS-1$
-        parts.add(obj.getCursorName());
-        parts.add("\n"); //$NON-NLS-1$
-        parts.add(registerNode(obj.getBlock()));
+    public void visit( LoopStatement obj ) {
+        append(LOOP);
+        append(" "); //$NON-NLS-1$
+        append(ON);
+        append(" ("); //$NON-NLS-1$
+        visitNode(obj.getCommand());
+        append(") "); //$NON-NLS-1$
+        append(AS);
+        append(" "); //$NON-NLS-1$
+        append(obj.getCursorName());
+        append("\n"); //$NON-NLS-1$
+        addTabs(0);
+        visitNode(obj.getBlock());
     }
 
-    public void visit(WhileStatement obj) {
-        parts.add(WHILE);
-        parts.add("("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCondition()));
-        parts.add(")\n"); //$NON-NLS-1$
-        parts.add(registerNode(obj.getBlock()));
+    public void visit( WhileStatement obj ) {
+        append(WHILE);
+        append("("); //$NON-NLS-1$
+        visitNode(obj.getCondition());
+        append(")\n"); //$NON-NLS-1$
+        addTabs(0);
+        visitNode(obj.getBlock());
     }
 
-    public void visit(ExistsCriteria obj) {
+    public void visit( ExistsCriteria obj ) {
         // operator and beginning of list
-        parts.add(EXISTS);
-        parts.add(" ("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCommand()));
-        parts.add(")"); //$NON-NLS-1$
+        append(EXISTS);
+        append(" ("); //$NON-NLS-1$
+        visitNode(obj.getCommand());
+        append(")"); //$NON-NLS-1$
     }
 
-    public void visit(SubqueryCompareCriteria obj){
+    public void visit( SubqueryCompareCriteria obj ) {
         Expression leftExpression = obj.getLeftExpression();
-        parts.add(registerNode(leftExpression));
+        visitNode(leftExpression);
 
         String operator = obj.getOperatorAsString();
         String quantifier = obj.getPredicateQuantifierAsString();
 
         // operator and beginning of list
-        parts.add(SPACE);
-        parts.add(operator);
-        parts.add(SPACE);
-        parts.add(quantifier);
-        parts.add("("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCommand()));
-        parts.add(")"); //$NON-NLS-1$
+        append(SPACE);
+        append(operator);
+        append(SPACE);
+        append(quantifier);
+        append("("); //$NON-NLS-1$
+        visitNode(obj.getCommand());
+        append(")"); //$NON-NLS-1$
     }
 
-    public void visit(ScalarSubquery obj) {
+    public void visit( ScalarSubquery obj ) {
         // operator and beginning of list
-        parts.add("("); //$NON-NLS-1$
-        parts.add(registerNode(obj.getCommand()));
-        parts.add(")"); //$NON-NLS-1$
+        append("("); //$NON-NLS-1$
+        visitNode(obj.getCommand());
+        append(")"); //$NON-NLS-1$
     }
-    
+
     @Override
-    public void visit(XMLAttributes obj) {
-    	parts.add(XMLATTRIBUTES);
-    	parts.add("("); //$NON-NLS-1$
-    	registerNodes(obj.getArgs(), 0);
-    	parts.add(")"); //$NON-NLS-1$
+    public void visit( XMLAttributes obj ) {
+        append(XMLATTRIBUTES);
+        append("("); //$NON-NLS-1$
+        registerNodes(obj.getArgs(), 0);
+        append(")"); //$NON-NLS-1$
     }
-    
+
     @Override
-    public void visit(XMLElement obj) {
-    	parts.add(XMLELEMENT);
-    	parts.add("(NAME "); //$NON-NLS-1$
-    	outputDisplayName(obj.getName());
-    	if (obj.getNamespaces() != null) {
-    		parts.add(", "); //$NON-NLS-1$
-    		parts.add(registerNode(obj.getNamespaces()));
-    	}
-    	if (obj.getAttributes() != null) {
-    		parts.add(", "); //$NON-NLS-1$
-    		parts.add(registerNode(obj.getAttributes()));
-    	}
-    	if (!obj.getContent().isEmpty()) {
-    		parts.add(", "); //$NON-NLS-1$
-    	}
-		registerNodes(obj.getContent(), 0);
-    	parts.add(")"); //$NON-NLS-1$
+    public void visit( XMLElement obj ) {
+        append(XMLELEMENT);
+        append("(NAME "); //$NON-NLS-1$
+        outputDisplayName(obj.getName());
+        if (obj.getNamespaces() != null) {
+            append(", "); //$NON-NLS-1$
+            visitNode(obj.getNamespaces());
+        }
+        if (obj.getAttributes() != null) {
+            append(", "); //$NON-NLS-1$
+            visitNode(obj.getAttributes());
+        }
+        if (!obj.getContent().isEmpty()) {
+            append(", "); //$NON-NLS-1$
+        }
+        registerNodes(obj.getContent(), 0);
+        append(")"); //$NON-NLS-1$
     }
-    
+
     @Override
-    public void visit(XMLForest obj) {
-    	parts.add(XMLFOREST);
-    	parts.add("("); //$NON-NLS-1$
-    	if (obj.getNamespaces() != null) {
-    		parts.add(registerNode(obj.getNamespaces()));
-    		parts.add(", "); //$NON-NLS-1$
-    	}
-    	registerNodes(obj.getArgs(), 0);
-    	parts.add(")"); //$NON-NLS-1$
+    public void visit( XMLForest obj ) {
+        append(XMLFOREST);
+        append("("); //$NON-NLS-1$
+        if (obj.getNamespaces() != null) {
+            visitNode(obj.getNamespaces());
+            append(", "); //$NON-NLS-1$
+        }
+        registerNodes(obj.getArgs(), 0);
+        append(")"); //$NON-NLS-1$
     }
-    
+
     @Override
-    public void visit(XMLNamespaces obj) {
-    	parts.add(XMLNAMESPACES);
-    	parts.add("("); //$NON-NLS-1$
-    	for (Iterator<NamespaceItem> items = obj.getNamespaceItems().iterator(); items.hasNext();) {
-    		NamespaceItem item = items.next();
-    		if (item.getPrefix() == null) {
-    			if (item.getUri() == null) {
-    				parts.add("NO DEFAULT"); //$NON-NLS-1$
-    			} else {
-	    			parts.add("DEFAULT "); //$NON-NLS-1$
-	    			parts.add(registerNode(new Constant(item.getUri())));
-    			}
-    		} else {
-    			parts.add(registerNode(new Constant(item.getUri())));
-    			parts.add(" AS "); //$NON-NLS-1$
-        		outputDisplayName(item.getPrefix());
-    		}
-    		if (items.hasNext()) {
-    			parts.add(", "); //$NON-NLS-1$
-    		}
-    	}
-    	parts.add(")"); //$NON-NLS-1$
+    public void visit( XMLNamespaces obj ) {
+        append(XMLNAMESPACES);
+        append("("); //$NON-NLS-1$
+        for (Iterator<NamespaceItem> items = obj.getNamespaceItems().iterator(); items.hasNext();) {
+            NamespaceItem item = items.next();
+            if (item.getPrefix() == null) {
+                if (item.getUri() == null) {
+                    append("NO DEFAULT"); //$NON-NLS-1$
+                } else {
+                    append("DEFAULT "); //$NON-NLS-1$
+                    visitNode(new Constant(item.getUri()));
+                }
+            } else {
+                visitNode(new Constant(item.getUri()));
+                append(" AS "); //$NON-NLS-1$
+                outputDisplayName(item.getPrefix());
+            }
+            if (items.hasNext()) {
+                append(", "); //$NON-NLS-1$
+            }
+        }
+        append(")"); //$NON-NLS-1$
     }
-    
-    public void visit(Limit obj) {
-        parts.add(LIMIT);
+
+    public void visit( Limit obj ) {
+        append(LIMIT);
         if (obj.getOffset() != null) {
-            parts.add(SPACE);
-            parts.add(registerNode(obj.getOffset()));
-            parts.add(","); //$NON-NLS-1$
+            append(SPACE);
+            visitNode(obj.getOffset());
+            append(","); //$NON-NLS-1$
         }
-        parts.add(SPACE);
-        parts.add(registerNode(obj.getRowLimit()));
+        append(SPACE);
+        visitNode(obj.getRowLimit());
     }
-    
+
     @Override
-    public void visit(TextTable obj) {
-    	parts.add("TEXTTABLE("); //$NON-NLS-1$
-    	parts.add(registerNode(obj.getFile()));
-    	parts.add(SPACE);
-    	parts.add(NonReserved.COLUMNS);
-    	
-    	for (Iterator<TextColumn> cols = obj.getColumns().iterator(); cols.hasNext();) {
-    		TextColumn col = cols.next();
-    		parts.add(SPACE);
-    		outputDisplayName(col.getName());
-    		parts.add(SPACE);
-    		parts.add(col.getType());
-    		if (col.getWidth() != null) {
-        		parts.add(SPACE);
-    			parts.add(NonReserved.WIDTH);
-            	parts.add(SPACE);
-    			parts.add(col.getWidth());
-    		}
-    		if (cols.hasNext()) {
-    			parts.add(","); //$NON-NLS-1$
-    		}
-		}
-    	if (obj.getDelimiter() != null) {
-        	parts.add(SPACE);
-    		parts.add(NonReserved.DELIMITER);
-        	parts.add(SPACE);
-    		parts.add(registerNode(new Constant(obj.getDelimiter())));
-    	}
-    	if (obj.getQuote() != null) {
-        	parts.add(SPACE);
-        	if (obj.isEscape()) {
-        		parts.add(ESCAPE);
-        	} else {
-        		parts.add(NonReserved.QUOTE);
-        	}
-        	parts.add(SPACE);
-    		parts.add(registerNode(new Constant(obj.getQuote())));
-    	}
-    	if (obj.getHeader() != null) {
-        	parts.add(SPACE);
-    		parts.add(NonReserved.HEADER);
-    		if (1 != obj.getHeader()) {
-	        	parts.add(SPACE);
-	    		parts.add(obj.getHeader());
-    		}
-    	}
-    	if (obj.getSkip() != null) {
-        	parts.add(SPACE);
-    		parts.add("SKIP"); //$NON-NLS-1$
-        	parts.add(SPACE);
-    		parts.add(obj.getSkip());
-    	}
-    	parts.add(")");//$NON-NLS-1$
-    	parts.add(SPACE);
-    	parts.add(AS);
-    	parts.add(SPACE);
-		outputDisplayName(obj.getName());
+    public void visit( TextTable obj ) {
+        append("TEXTTABLE("); //$NON-NLS-1$
+        visitNode(obj.getFile());
+        append(SPACE);
+        append(NonReserved.COLUMNS);
+
+        for (Iterator<TextColumn> cols = obj.getColumns().iterator(); cols.hasNext();) {
+            TextColumn col = cols.next();
+            append(SPACE);
+            outputDisplayName(col.getName());
+            append(SPACE);
+            append(col.getType());
+            if (col.getWidth() != null) {
+                append(SPACE);
+                append(NonReserved.WIDTH);
+                append(SPACE);
+                append(col.getWidth());
+            }
+            if (cols.hasNext()) {
+                append(","); //$NON-NLS-1$
+            }
+        }
+        if (obj.getDelimiter() != null) {
+            append(SPACE);
+            append(NonReserved.DELIMITER);
+            append(SPACE);
+            visitNode(new Constant(obj.getDelimiter()));
+        }
+        if (obj.getQuote() != null) {
+            append(SPACE);
+            if (obj.isEscape()) {
+                append(ESCAPE);
+            } else {
+                append(NonReserved.QUOTE);
+            }
+            append(SPACE);
+            visitNode(new Constant(obj.getQuote()));
+        }
+        if (obj.getHeader() != null) {
+            append(SPACE);
+            append(NonReserved.HEADER);
+            if (1 != obj.getHeader()) {
+                append(SPACE);
+                append(obj.getHeader());
+            }
+        }
+        if (obj.getSkip() != null) {
+            append(SPACE);
+            append("SKIP"); //$NON-NLS-1$
+            append(SPACE);
+            append(obj.getSkip());
+        }
+        append(")");//$NON-NLS-1$
+        append(SPACE);
+        append(AS);
+        append(SPACE);
+        outputDisplayName(obj.getName());
     }
 
     @Override
-    public void visit(XMLTable obj) {
-    	parts.add("XMLTABLE("); //$NON-NLS-1$
-    	if (obj.getNamespaces() != null) {
-    		parts.add(registerNode(obj.getNamespaces()));
-    		parts.add(","); //$NON-NLS-1$
-    		parts.add(SPACE);
-    	}
-    	parts.add(new Constant(obj.getXquery()));
-    	if (!obj.getPassing().isEmpty()) {
-    		parts.add(SPACE);
-        	parts.add(NonReserved.PASSING);
-        	parts.add(SPACE);
-	    	registerNodes(obj.getPassing(), 0);
-    	}
-    	if (!obj.getColumns().isEmpty()) {
-    		parts.add(SPACE);
-        	parts.add(NonReserved.COLUMNS);
-	    	for (Iterator<XMLColumn> cols = obj.getColumns().iterator(); cols.hasNext();) {
-	    		XMLColumn col = cols.next();
-	    		parts.add(SPACE);
-	    		outputDisplayName(col.getName());
-	    		parts.add(SPACE);
-	    		if (col.isOrdinal()) {
-	    			parts.add(FOR);
-	    			parts.add(SPACE);
-	    			parts.add(NonReserved.ORDINALITY); 
-	    		} else {
-	    			parts.add(col.getType());
-		    		if (col.getDefaultExpression() != null) {
-		        		parts.add(SPACE);
-		    			parts.add(DEFAULT);
-		            	parts.add(SPACE);
-		    			parts.add(registerNode(col.getDefaultExpression()));
-		    		}
-		    		if (col.getPath() != null) {
-		        		parts.add(SPACE);
-		    			parts.add(NonReserved.PATH); 
-		            	parts.add(SPACE);
-		    			parts.add(new Constant(col.getPath()));
-		    		}
-	    		}
-	    		if (cols.hasNext()) {
-	    			parts.add(","); //$NON-NLS-1$
-	    		}
-			}
-    	}
-    	parts.add(")");//$NON-NLS-1$
-    	parts.add(SPACE);
-    	parts.add(AS);
-    	parts.add(SPACE);
-		outputDisplayName(obj.getName());
+    public void visit( XMLTable obj ) {
+        append("XMLTABLE("); //$NON-NLS-1$
+        if (obj.getNamespaces() != null) {
+            visitNode(obj.getNamespaces());
+            append(","); //$NON-NLS-1$
+            append(SPACE);
+        }
+        visitNode(new Constant(obj.getXquery()));
+        if (!obj.getPassing().isEmpty()) {
+            append(SPACE);
+            append(NonReserved.PASSING);
+            append(SPACE);
+            registerNodes(obj.getPassing(), 0);
+        }
+        if (!obj.getColumns().isEmpty()) {
+            append(SPACE);
+            append(NonReserved.COLUMNS);
+            for (Iterator<XMLColumn> cols = obj.getColumns().iterator(); cols.hasNext();) {
+                XMLColumn col = cols.next();
+                append(SPACE);
+                outputDisplayName(col.getName());
+                append(SPACE);
+                if (col.isOrdinal()) {
+                    append(FOR);
+                    append(SPACE);
+                    append(NonReserved.ORDINALITY);
+                } else {
+                    append(col.getType());
+                    if (col.getDefaultExpression() != null) {
+                        append(SPACE);
+                        append(DEFAULT);
+                        append(SPACE);
+                        visitNode(col.getDefaultExpression());
+                    }
+                    if (col.getPath() != null) {
+                        append(SPACE);
+                        append(NonReserved.PATH);
+                        append(SPACE);
+                        visitNode(new Constant(col.getPath()));
+                    }
+                }
+                if (cols.hasNext()) {
+                    append(","); //$NON-NLS-1$
+                }
+            }
+        }
+        append(")");//$NON-NLS-1$
+        append(SPACE);
+        append(AS);
+        append(SPACE);
+        outputDisplayName(obj.getName());
     }
-    
+
     @Override
-    public void visit(XMLQuery obj) {
-    	parts.add("XMLQUERY("); //$NON-NLS-1$
-    	if (obj.getNamespaces() != null) {
-    		parts.add(registerNode(obj.getNamespaces()));
-    		parts.add(","); //$NON-NLS-1$
-    		parts.add(SPACE);
-    	}
-    	parts.add(new Constant(obj.getXquery()));
-    	if (!obj.getPassing().isEmpty()) {
-    		parts.add(SPACE);
-        	parts.add(NonReserved.PASSING);
-        	parts.add(SPACE);
-	    	registerNodes(obj.getPassing(), 0);
-    	}
-    	if (obj.getEmptyOnEmpty() != null) {
-    		parts.add(SPACE);
-    		if (obj.getEmptyOnEmpty()) {
-        		parts.add(NonReserved.EMPTY);    			
-    		} else {
-    			parts.add(NULL);
-    		}
-    		parts.add(SPACE);
-    		parts.add(ON);
-    		parts.add(SPACE);
-    		parts.add(NonReserved.EMPTY);
-    	}
-    	parts.add(")");//$NON-NLS-1$
+    public void visit( XMLQuery obj ) {
+        append("XMLQUERY("); //$NON-NLS-1$
+        if (obj.getNamespaces() != null) {
+            visitNode(obj.getNamespaces());
+            append(","); //$NON-NLS-1$
+            append(SPACE);
+        }
+        visitNode(new Constant(obj.getXquery()));
+        if (!obj.getPassing().isEmpty()) {
+            append(SPACE);
+            append(NonReserved.PASSING);
+            append(SPACE);
+            registerNodes(obj.getPassing(), 0);
+        }
+        if (obj.getEmptyOnEmpty() != null) {
+            append(SPACE);
+            if (obj.getEmptyOnEmpty()) {
+                append(NonReserved.EMPTY);
+            } else {
+                append(NULL);
+            }
+            append(SPACE);
+            append(ON);
+            append(SPACE);
+            append(NonReserved.EMPTY);
+        }
+        append(")");//$NON-NLS-1$
     }
-    
+
     @Override
-    public void visit(DerivedColumn obj) {
-    	parts.add(registerNode(obj.getExpression()));
-    	if (obj.getAlias() != null) {
-    		parts.add(SPACE);
-    		parts.add(AS);
-    		parts.add(SPACE);
-    		outputDisplayName(obj.getAlias());
-    	}
+    public void visit( DerivedColumn obj ) {
+        visitNode(obj.getExpression());
+        if (obj.getAlias() != null) {
+            append(SPACE);
+            append(AS);
+            append(SPACE);
+            outputDisplayName(obj.getAlias());
+        }
     }
-    
+
     @Override
-    public void visit(XMLSerialize obj) {
-    	parts.add(XMLSERIALIZE);
-    	parts.add(Tokens.LPAREN);
-    	if (obj.isDocument() != null) {
-	    	if (obj.isDocument()) {
-	    		parts.add(NonReserved.DOCUMENT);
-	    	} else {
-	    		parts.add(NonReserved.CONTENT);
-	    	}
-	    	parts.add(SPACE);
-    	}
-    	parts.add(registerNode(obj.getExpression()));
-    	if (obj.getTypeString() != null) {
-    		parts.add(SPACE);
-        	parts.add(AS);
-        	parts.add(SPACE);
-        	parts.add(obj.getTypeString());
-    	}
-    	parts.add(Tokens.RPAREN);
+    public void visit( XMLSerialize obj ) {
+        append(XMLSERIALIZE);
+        append(Tokens.LPAREN);
+        if (obj.isDocument() != null) {
+            if (obj.isDocument()) {
+                append(NonReserved.DOCUMENT);
+            } else {
+                append(NonReserved.CONTENT);
+            }
+            append(SPACE);
+        }
+        visitNode(obj.getExpression());
+        if (obj.getTypeString() != null) {
+            append(SPACE);
+            append(AS);
+            append(SPACE);
+            append(obj.getTypeString());
+        }
+        append(Tokens.RPAREN);
     }
-    
+
     @Override
-    public void visit(QueryString obj) {
-    	parts.add(NonReserved.QUERYSTRING);
-    	parts.add("("); //$NON-NLS-1$
-    	parts.add(registerNode(obj.getPath()));
-    	if (!obj.getArgs().isEmpty()) {
-    		parts.add(","); //$NON-NLS-1$
-	    	parts.add(SPACE);
-	    	registerNodes(obj.getArgs(), 0);
-    	}
-    	parts.add(")"); //$NON-NLS-1$
+    public void visit( QueryString obj ) {
+        append(NonReserved.QUERYSTRING);
+        append("("); //$NON-NLS-1$
+        visitNode(obj.getPath());
+        if (!obj.getArgs().isEmpty()) {
+            append(","); //$NON-NLS-1$
+            append(SPACE);
+            registerNodes(obj.getArgs(), 0);
+        }
+        append(")"); //$NON-NLS-1$
     }
-    
+
     @Override
-    public void visit(XMLParse obj) {
-    	parts.add(XMLPARSE);
-    	parts.add(Tokens.LPAREN);
-    	if (obj.isDocument()) {
-    		parts.add(NonReserved.DOCUMENT);
-    	} else {
-    		parts.add(NonReserved.CONTENT);
-    	}
-    	parts.add(SPACE);
-    	parts.add(registerNode(obj.getExpression()));
-    	if (obj.isWellFormed()) {
-    		parts.add(SPACE);
-    		parts.add(NonReserved.WELLFORMED);
-    	}
-    	parts.add(Tokens.RPAREN);
+    public void visit( XMLParse obj ) {
+        append(XMLPARSE);
+        append(Tokens.LPAREN);
+        if (obj.isDocument()) {
+            append(NonReserved.DOCUMENT);
+        } else {
+            append(NonReserved.CONTENT);
+        }
+        append(SPACE);
+        visitNode(obj.getExpression());
+        if (obj.isWellFormed()) {
+            append(SPACE);
+            append(NonReserved.WELLFORMED);
+        }
+        append(Tokens.RPAREN);
     }
-    
+
     @Override
-    public void visit(ExpressionCriteria obj) {
-    	obj.getExpression().acceptVisitor(this);
+    public void visit( ExpressionCriteria obj ) {
+        visitNode(obj.getExpression());
     }
 
-    public static String escapeSinglePart(String part) {
-    	if(isReservedWord(part)) {
-    	    return ID_ESCAPE_CHAR + part + ID_ESCAPE_CHAR;
-    	}
-    	boolean escape = true;
-    	char start = part.charAt(0);
-    	if (start == '#' || start == '@' || StringUtil.isLetter(start)) {
-    		escape = false;
-    		for (int i = 1; !escape && i < part.length(); i++) {
-    			char c = part.charAt(i);
-    			escape = !StringUtil.isLetterOrDigit(c) && c != '_';
-    		}
-    	}
-    	if (escape) {
-    		return ID_ESCAPE_CHAR + escapeStringValue(part, "\"") + ID_ESCAPE_CHAR; //$NON-NLS-1$
-    	}
-    	return part;
+    public static String escapeSinglePart( String part ) {
+        if (isReservedWord(part)) {
+            return ID_ESCAPE_CHAR + part + ID_ESCAPE_CHAR;
+        }
+        boolean escape = true;
+        char start = part.charAt(0);
+        if (start == '#' || start == '@' || StringUtil.isLetter(start)) {
+            escape = false;
+            for (int i = 1; !escape && i < part.length(); i++) {
+                char c = part.charAt(i);
+                escape = !StringUtil.isLetterOrDigit(c) && c != '_';
+            }
+        }
+        if (escape) {
+            return ID_ESCAPE_CHAR + escapeStringValue(part, "\"") + ID_ESCAPE_CHAR; //$NON-NLS-1$
+        }
+        return part;
     }
 
     /**
-     * Check whether a string is considered a reserved word or not.  Subclasses
-     * may override to change definition of reserved word.
+     * Check whether a string is considered a reserved word or not. Subclasses may override to change definition of reserved word.
+     * 
      * @param string String to check
      * @return True if reserved word
      */
-    static boolean isReservedWord(String string) {
-    	if(string == null) {
-    	    return false;
-    	}
-   		return SQLConstants.isReservedWord(string);
+    static boolean isReservedWord( String string ) {
+        if (string == null) {
+            return false;
+        }
+        return SQLConstants.isReservedWord(string);
     }
-    
+
 }

Modified: branches/7.1.x/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java
===================================================================
--- branches/7.1.x/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java	2010-08-23 17:53:50 UTC (rev 2484)
+++ branches/7.1.x/engine/src/test/java/org/teiid/query/optimizer/TestOptimizer.java	2010-08-23 19:19:45 UTC (rev 2485)
@@ -4667,7 +4667,7 @@
         ProcessorPlan plan = helpPlan(sql,  
                                       FakeMetadataFactory.exampleBQTCached(),
                                       null, capFinder,
-                                      new String[] {"(SELECT g_2.intkey AS c_0, 'a' AS c_1 FROM BQT1.SmallA AS g_2 UNION ALL SELECT g_1.IntKey AS c_0, 'b' AS c_1 FROM BQT1.SmallA AS g_1) UNION ALL SELECT g_0.IntKey AS c_0, 'c' AS c_1 FROM BQT1.SmallA AS g_0"}, //$NON-NLS-1$ 
+                                      new String[] {"SELECT g_2.intkey AS c_0, 'a' AS c_1 FROM BQT1.SmallA AS g_2 UNION ALL SELECT g_1.IntKey AS c_0, 'b' AS c_1 FROM BQT1.SmallA AS g_1 UNION ALL SELECT g_0.IntKey AS c_0, 'c' AS c_1 FROM BQT1.SmallA AS g_0"}, //$NON-NLS-1$ 
                                       ComparisonMode.EXACT_COMMAND_STRING );
 
         checkNodeTypes(plan, FULL_PUSHDOWN);        

Modified: branches/7.1.x/engine/src/test/java/org/teiid/query/parser/TestSetQueryParsing.java
===================================================================
--- branches/7.1.x/engine/src/test/java/org/teiid/query/parser/TestSetQueryParsing.java	2010-08-23 17:53:50 UTC (rev 2484)
+++ branches/7.1.x/engine/src/test/java/org/teiid/query/parser/TestSetQueryParsing.java	2010-08-23 19:19:45 UTC (rev 2485)
@@ -197,7 +197,7 @@
         setQuery = new SetQuery(Operation.UNION, false, setQuery, query);
 
         TestParser.helpTest("select c1 from g1 union select c2 from g2 union select c3 from g3",  //$NON-NLS-1$
-                 "(SELECT c1 FROM g1 UNION SELECT c2 FROM g2) UNION SELECT c3 FROM g3",  //$NON-NLS-1$
+                 "SELECT c1 FROM g1 UNION SELECT c2 FROM g2 UNION SELECT c3 FROM g3",  //$NON-NLS-1$
                  setQuery);
     }
     
@@ -258,7 +258,7 @@
         setQuery = new SetQuery(SetQuery.Operation.UNION, false, setQuery, query);
 
         TestParser.helpTest("select c1 from g1 union select c2 from g2 union all select c3 from g3 union select c4 from g4",  //$NON-NLS-1$
-                 "((SELECT c1 FROM g1 UNION SELECT c2 FROM g2) UNION ALL SELECT c3 FROM g3) UNION SELECT c4 FROM g4",  //$NON-NLS-1$
+                 "SELECT c1 FROM g1 UNION SELECT c2 FROM g2 UNION ALL SELECT c3 FROM g3 UNION SELECT c4 FROM g4",  //$NON-NLS-1$
                  setQuery);
     }   
     



More information about the teiid-commits mailing list