package com.chenyang.druid.sql.dialect.sqlserver.visitor;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLName;
import com.chenyang.druid.sql.ast.SQLObjectImpl;
import com.chenyang.druid.sql.ast.SQLOrderBy;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.SQLWindow;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.expr.SQLSequenceExpr;
import com.chenyang.druid.sql.ast.statement.SQLAlterTableAddColumn;
import com.chenyang.druid.sql.ast.statement.SQLAssignItem;
import com.chenyang.druid.sql.ast.statement.SQLBlockStatement;
import com.chenyang.druid.sql.ast.statement.SQLColumnConstraint;
import com.chenyang.druid.sql.ast.statement.SQLColumnDefinition;
import com.chenyang.druid.sql.ast.statement.SQLCommitStatement;
import com.chenyang.druid.sql.ast.statement.SQLCreateUserStatement;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLGrantStatement;
import com.chenyang.druid.sql.ast.statement.SQLInsertStatement;
import com.chenyang.druid.sql.ast.statement.SQLScriptCommitStatement;
import com.chenyang.druid.sql.ast.statement.SQLSelect;
import com.chenyang.druid.sql.ast.statement.SQLSelectGroupByClause;
import com.chenyang.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.chenyang.druid.sql.ast.statement.SQLSetStatement;
import com.chenyang.druid.sql.ast.statement.SQLStartTransactionStatement;
import com.chenyang.druid.sql.ast.statement.SQLTableSampling;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLWithSubqueryClause;
import com.chenyang.druid.sql.dialect.sqlserver.ast.SQLServerOutput;
import com.chenyang.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
import com.chenyang.druid.sql.dialect.sqlserver.ast.SQLServerTop;
import com.chenyang.druid.sql.dialect.sqlserver.ast.clause.SQLServerOver;
import com.chenyang.druid.sql.dialect.sqlserver.ast.expr.SQLServerCurrentOfCursorExpr;
import com.chenyang.druid.sql.dialect.sqlserver.ast.expr.SQLServerDateTimeExpr;
import com.chenyang.druid.sql.dialect.sqlserver.ast.expr.SQLServerObjectReferenceExpr;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerDeleteStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerExecStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerMergeStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerPivotBase;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerRollbackStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectPivot;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectSubqueryTableSource;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectTableReference;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSelectUnPivot;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerSetTransactionIsolationLevelStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUnionQueryTableSource;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateSetItem;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerValuesTableSource;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerWaitForStatement;
import com.chenyang.druid.sql.parser.ParserException;
import com.chenyang.druid.sql.visitor.SQLASTOutputVisitor;
import com.chenyang.druid.util.FnvHash;
import java.util.List;

public class SQLServerOutputVisitor extends SQLASTOutputVisitor implements SQLServerASTVisitor {
   public SQLServerOutputVisitor(Appendable appender) {
      super(appender, DbType.sqlserver);
   }

   public SQLServerOutputVisitor(Appendable appender, boolean parameterized) {
      super(appender, parameterized);
      this.dbType = DbType.sqlserver;
   }

   public boolean visit(SQLServerSelectQueryBlock x) {
      this.print0(this.ucase ? "SELECT " : "select ");
      if (1 == x.getDistionOption()) {
         this.print0(this.ucase ? "ALL " : "all ");
      } else if (2 == x.getDistionOption()) {
         this.print0(this.ucase ? "DISTINCT " : "distinct ");
      } else if (3 == x.getDistionOption()) {
         this.print0(this.ucase ? "UNIQUE " : "unique ");
      }

      SQLServerTop top = x.getTop();
      if (top != null) {
         this.visit(top);
         this.print(' ');
      }

      this.printSelectList(x.getSelectList());
      SQLExprTableSource into = x.getInto();
      if (into != null) {
         this.println();
         this.print0(this.ucase ? "INTO " : "into ");
         this.printTableSource(into);
         if (x.getOn() != null) {
            this.print0(this.ucase ? " ON " : " on ");
            x.getOn().accept(this);
         }
      }

      SQLTableSource from = x.getFrom();
      if (from != null) {
         this.println();
         this.print0(this.ucase ? "FROM " : "from ");
         this.printTableSource(from);
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.println();
         this.print0(this.ucase ? "WHERE " : "where ");
         this.printExpr(where);
      }

      SQLSelectGroupByClause groupBy = x.getGroupBy();
      if (groupBy != null) {
         this.println();
         this.visit((SQLSelectGroupByClause)groupBy);
      }

      List<SQLWindow> windows = x.getWindows();
      if (windows != null && windows.size() > 0) {
         this.println();
         this.print0(this.ucase ? "WINDOW " : "window ");
         this.printAndAccept(windows, ", ");
      }

      SQLOrderBy orderBy = x.getOrderBy();
      if (orderBy != null) {
         this.println();
         this.visit((SQLOrderBy)orderBy);
      }

      this.printFetchFirst(x);
      return false;
   }

   public void endVisit(SQLServerSelectQueryBlock x) {
      this.endVisit((SQLSelectQueryBlock)x);
   }

   public boolean visit(SQLServerTop x) {
      boolean parameterized = this.parameterized;
      this.parameterized = false;
      this.print0(this.ucase ? "TOP " : "top ");
      this.print('(');
      x.getExpr().accept(this);
      this.print(')');
      if (x.isPercent()) {
         this.print0(this.ucase ? " PERCENT" : " percent");
      }

      if (x.isWithTies()) {
         this.print0(this.ucase ? " WITH TIES" : " with ties");
      }

      this.parameterized = parameterized;
      return false;
   }

   public void endVisit(SQLServerTop x) {
   }

   public boolean visit(SQLServerObjectReferenceExpr x) {
      this.print0(x.toString());
      return false;
   }

   public void endVisit(SQLServerObjectReferenceExpr x) {
   }

   public boolean visit(SQLServerInsertStatement x) {
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         this.visit((SQLWithSubqueryClause)with);
         this.println();
      }

      this.print0(this.ucase ? "INSERT " : "insert ");
      if (x.getTop() != null) {
         x.getTop().setParent(x);
         x.getTop().accept(this);
         this.print(' ');
      }

      this.print0(this.ucase ? "INTO " : "into ");
      x.getTableSource().accept(this);
      this.printInsertColumns(x.getColumns());
      if (x.getOutput() != null) {
         this.println();
         x.getOutput().setParent(x);
         x.getOutput().accept(this);
      }

      if (x.getValuesList().size() != 0) {
         this.println();
         this.print0(this.ucase ? "VALUES " : "values ");
         int i = 0;

         for(int size = x.getValuesList().size(); i < size; ++i) {
            if (i != 0) {
               this.print(',');
               this.println();
            }

            ((SQLInsertStatement.ValuesClause)x.getValuesList().get(i)).accept(this);
         }
      }

      if (x.getQuery() != null) {
         this.println();
         x.getQuery().accept(this);
      }

      if (x.isDefaultValues()) {
         this.print0(this.ucase ? " DEFAULT VALUES" : " default values");
      }

      return false;
   }

   public void endVisit(SQLServerInsertStatement x) {
      this.endVisit((SQLInsertStatement)x);
   }

   public boolean visit(SQLServerUpdateStatement x) {
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         this.visit((SQLWithSubqueryClause)with);
         this.println();
      }

      this.print0(this.ucase ? "UPDATE " : "update ");
      SQLServerTop top = x.getTop();
      if (top != null) {
         top.accept(this);
         this.print(' ');
      }

      this.printTableSource(x.getTableSource());
      this.println();
      this.print0(this.ucase ? "SET " : "set ");
      int i = 0;

      for(int size = x.getItems().size(); i < size; ++i) {
         if (i != 0) {
            this.print0(", ");
         }

         SQLServerUpdateSetItem item = (SQLServerUpdateSetItem)x.getItems().get(i);
         this.visit(item);
      }

      SQLServerOutput output = x.getOutput();
      if (output != null) {
         this.println();
         this.visit(output);
      }

      SQLTableSource from = x.getFrom();
      if (from != null) {
         this.println();
         this.print0(this.ucase ? "FROM " : "from ");
         this.printTableSource(from);
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.println();
         ++this.indentCount;
         this.print0(this.ucase ? "WHERE " : "where ");
         this.printExpr(where);
         --this.indentCount;
      }

      return false;
   }

   public void endVisit(SQLServerUpdateStatement x) {
   }

   public boolean visit(SQLExprTableSource x) {
      this.printTableSourceExpr(x.getExpr());
      SQLTableSampling sampling = x.getSampling();
      if (sampling != null) {
         this.print(' ');
         sampling.accept(this);
      }

      String alias = x.getAlias();
      if (alias != null) {
         this.print0(this.ucase ? " AS " : " as ");
         this.print0(alias);
      }

      if (x.getHints() != null && x.getHints().size() > 0) {
         this.print0(this.ucase ? " WITH (" : " with (");
         this.printAndAccept(x.getHints(), ", ");
         this.print(')');
      }

      return false;
   }

   public boolean visit(SQLServerSelectTableReference x) {
      this.visit((SQLExprTableSource)x);
      this.printPivot(x.getPivot(), x.getPivotAlias());
      return false;
   }

   public void endVisit(SQLServerSelectTableReference x) {
   }

   public boolean visit(SQLColumnDefinition x) {
      boolean parameterized = this.parameterized;
      this.parameterized = false;
      x.getName().accept(this);
      if (x.getDataType() != null) {
         this.print(' ');
         x.getDataType().accept(this);
      }

      if (x.getDefaultExpr() != null) {
         this.visitColumnDefault(x);
      }

      for(SQLColumnConstraint item : x.getConstraints()) {
         this.print(' ');
         item.accept(this);
      }

      SQLColumnDefinition.Identity identity = x.getIdentity();
      if (identity != null) {
         this.print(' ');
         identity.accept(this);
      }

      if (x.getEnable() != null && x.getEnable()) {
         this.print0(this.ucase ? " ENABLE" : " enable");
      }

      this.parameterized = parameterized;
      return false;
   }

   public boolean visit(SQLServerExecStatement x) {
      this.print0(this.ucase ? "EXEC " : "exec ");
      SQLName returnStatus = x.getReturnStatus();
      if (returnStatus != null) {
         returnStatus.accept(this);
         this.print0(" = ");
      }

      SQLName moduleName = x.getModuleName();
      if (moduleName != null) {
         moduleName.accept(this);
         this.print(' ');
      } else {
         this.print0(" (");
      }

      this.printAndAccept(x.getParameters(), ", ");
      if (moduleName == null) {
         this.print(')');
      }

      return false;
   }

   public void endVisit(SQLServerExecStatement x) {
   }

   public boolean visit(SQLServerSetTransactionIsolationLevelStatement x) {
      this.print0(this.ucase ? "SET TRANSACTION ISOLATION LEVEL " : "set transaction isolation level ");
      this.print0(x.getLevel());
      return false;
   }

   public void endVisit(SQLServerSetTransactionIsolationLevelStatement x) {
   }

   public boolean visit(SQLSetStatement x) {
      this.print0(this.ucase ? "SET " : "set ");
      SQLSetStatement.Option option = x.getOption();
      if (option != null) {
         this.print(option.name());
         this.print(' ');
      }

      List<SQLAssignItem> items = x.getItems();

      for(int i = 0; i < items.size(); ++i) {
         if (i != 0) {
            this.print0(", ");
         }

         SQLAssignItem item = (SQLAssignItem)x.getItems().get(i);
         item.getTarget().accept(this);
         SQLExpr value = item.getValue();
         if (!(value instanceof SQLIdentifierExpr) || ((SQLIdentifierExpr)value).nameHashCode64() != FnvHash.Constants.ON && ((SQLIdentifierExpr)value).nameHashCode64() != FnvHash.Constants.OFF) {
            this.print0(" = ");
         } else {
            this.print(' ');
         }

         value.accept(this);
      }

      return false;
   }

   public boolean visit(SQLServerOutput x) {
      this.print0(this.ucase ? "OUTPUT " : "output ");
      this.printSelectList(x.getSelectList());
      if (x.getInto() != null) {
         ++this.indentCount;
         this.println();
         this.print0(this.ucase ? "INTO " : "into ");
         x.getInto().accept(this);
         if (x.getColumns().size() > 0) {
            ++this.indentCount;
            this.println();
            this.print('(');
            int i = 0;

            for(int size = x.getColumns().size(); i < size; ++i) {
               if (i != 0) {
                  if (i % 5 == 0) {
                     this.println();
                  }

                  this.print0(", ");
               }

               ((SQLExpr)x.getColumns().get(i)).accept(this);
            }

            this.print(')');
            --this.indentCount;
         }
      }

      --this.indentCount;
      return false;
   }

   public void endVisit(SQLServerOutput x) {
   }

   public boolean visit(SQLBlockStatement x) {
      this.print0(this.ucase ? "BEGIN" : "begin");
      ++this.indentCount;
      this.println();
      int i = 0;

      for(int size = x.getStatementList().size(); i < size; ++i) {
         if (i != 0) {
            this.println();
         }

         SQLStatement stmt = (SQLStatement)x.getStatementList().get(i);
         stmt.setParent(x);
         stmt.accept(this);
         this.print(';');
      }

      --this.indentCount;
      this.println();
      this.print0(this.ucase ? "END" : "end");
      return false;
   }

   protected void printGrantOn(SQLGrantStatement x) {
      if (x.getResource() != null) {
         this.print0(this.ucase ? " ON " : " on ");
         if (x.getResourceType() != null) {
            this.print0(x.getResourceType().name());
            this.print0("::");
         }

         x.getResource().accept(this);
      }

   }

   public boolean visit(SQLSelect x) {
      super.visit(x);
      if (x.isForBrowse()) {
         this.println();
         this.print0(this.ucase ? "FOR BROWSE" : "for browse");
      }

      if (x.getForXmlOptionsSize() > 0) {
         this.println();
         this.print0(this.ucase ? "FOR XML " : "for xml ");

         for(int i = 0; i < x.getForXmlOptions().size(); ++i) {
            if (i != 0) {
               this.print0(", ");
               this.print0((String)x.getForXmlOptions().get(i));
            }
         }
      }

      if (x.getXmlPath() != null) {
         this.println();
         this.print0(this.ucase ? "FOR XML " : "for xml ");
         x.getXmlPath().accept(this);
      }

      if (x.getOffset() != null) {
         this.println();
         this.print0(this.ucase ? "OFFSET " : "offset ");
         x.getOffset().accept(this);
         this.print0(this.ucase ? " ROWS" : " rows");
         if (x.getRowCount() != null) {
            this.print0(this.ucase ? " FETCH NEXT " : " fetch next ");
            x.getRowCount().accept(this);
            this.print0(this.ucase ? " ROWS ONLY" : " rows only");
         }
      }

      return false;
   }

   public boolean visit(SQLCommitStatement x) {
      this.print0(this.ucase ? "COMMIT" : "commit");
      if (x.isWork()) {
         this.print0(this.ucase ? " WORK" : " work");
      } else {
         this.print0(this.ucase ? " TRANSACTION" : " transaction");
         if (x.getTransactionName() != null) {
            this.print(' ');
            x.getTransactionName().accept(this);
         }

         if (x.getDelayedDurability() != null) {
            this.print0(this.ucase ? " WITH ( DELAYED_DURABILITY = " : " with ( delayed_durability = ");
            x.getDelayedDurability().accept(this);
            this.print0(" )");
         }
      }

      return false;
   }

   public boolean visit(SQLServerRollbackStatement x) {
      this.print0(this.ucase ? "ROLLBACK" : "rollback");
      if (x.isWork()) {
         this.print0(this.ucase ? " WORK" : " work");
      } else {
         this.print0(this.ucase ? " TRANSACTION" : " transaction");
         if (x.getName() != null) {
            this.print(' ');
            x.getName().accept(this);
         }
      }

      return false;
   }

   public void endVisit(SQLServerRollbackStatement x) {
   }

   public boolean visit(SQLServerWaitForStatement x) {
      this.print0(this.ucase ? "WAITFOR" : "waitfor");
      if (x.getDelay() != null) {
         this.print0(this.ucase ? " DELAY " : " delay ");
         x.getDelay().accept(this);
      } else if (x.getTime() != null) {
         this.print0(this.ucase ? " TIME " : " time ");
         x.getTime().accept(this);
      }

      if (x.getStatement() != null) {
         this.print0(this.ucase ? " DELAY " : " delay ");
         x.getStatement().accept(this);
      }

      if (x.getTimeout() != null) {
         this.print0(this.ucase ? " ,TIMEOUT " : " ,timeout ");
         x.getTimeout().accept(this);
      }

      return false;
   }

   public void endVisit(SQLServerWaitForStatement x) {
   }

   public boolean visit(SQLServerExecStatement.SQLServerParameter x) {
      x.getExpr().accept(this);
      if (x.getType()) {
         this.print0(this.ucase ? " OUT" : " out");
      }

      return false;
   }

   public void endVisit(SQLServerExecStatement.SQLServerParameter x) {
   }

   public boolean visit(SQLStartTransactionStatement x) {
      this.print0(this.ucase ? "BEGIN TRANSACTION" : "begin transaction");
      if (x.getName() != null) {
         this.print(' ');
         x.getName().accept(this);
      }

      return false;
   }

   public boolean visit(SQLScriptCommitStatement x) {
      this.print0(this.ucase ? "GO" : "go");
      return false;
   }

   public boolean visit(SQLCreateUserStatement x) {
      this.print0(this.ucase ? "CREATE USER " : "create user ");
      x.getUser().accept(this);
      this.print0(this.ucase ? " WITH PASSWORD = " : " with password = ");
      SQLExpr passoword = x.getPassword();
      if (passoword instanceof SQLIdentifierExpr) {
         this.print('\'');
         passoword.accept(this);
         this.print('\'');
      } else {
         passoword.accept(this);
      }

      return false;
   }

   public boolean visit(SQLSequenceExpr x) {
      SQLSequenceExpr.Function function = x.getFunction();
      switch (function) {
         case NextVal:
            this.print0(this.ucase ? "NEXT VALUE FOR " : "next value for ");
            this.printExpr(x.getSequence());
            return false;
         default:
            throw new ParserException("not support function : " + function);
      }
   }

   public boolean visit(SQLAlterTableAddColumn x) {
      boolean odps = this.isOdps();
      this.print0(this.ucase ? "ADD " : "add ");
      this.printAndAccept(x.getColumns(), ", ");
      return false;
   }

   public boolean visit(SQLServerUpdateSetItem x) {
      this.printExpr(x.getColumn(), this.parameterized);
      this.print0(" ");
      this.print0(x.getOperator().name);
      this.print0(" ");
      this.printExpr(x.getValue(), this.parameterized);
      return false;
   }

   public void endVisit(SQLServerUpdateSetItem x) {
   }

   public boolean visit(SQLServerDeleteStatement x) {
      SQLTableSource from = x.getFrom();
      String alias = x.getAlias();
      if (x.getWith() != null) {
         x.getWith().accept(this);
         this.println();
      }

      SQLServerTop top = x.getTop();
      if (from == null) {
         this.print0(this.ucase ? "DELETE " : "delete ");
         if (top != null) {
            this.visit(top);
            this.print(' ');
         }

         this.print0(this.ucase ? "FROM " : "from ");
         x.getExprTableSource().accept(this);
         if (alias != null) {
            this.print(' ');
            this.print0(alias);
         }

         if (x.getOutput() != null) {
            this.println();
            x.getOutput().accept(this);
         }
      } else {
         this.print0(this.ucase ? "DELETE " : "delete ");
         if (top != null) {
            this.visit(top);
            this.print(' ');
         }

         x.getExprTableSource().accept(this);
         if (x.getOutput() != null) {
            this.println();
            x.getOutput().accept(this);
         }

         this.println();
         this.print0(this.ucase ? " FROM " : " from ");
         from.accept(this);
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.println();
         this.print0(this.ucase ? "WHERE " : "where ");
         ++this.indentCount;
         where.accept(this);
         --this.indentCount;
      }

      return false;
   }

   public void endVisit(SQLServerDeleteStatement x) {
   }

   public boolean visit(SQLServerDateTimeExpr x) {
      x.getInputdate().accept(this);
      this.print(" AT TIME ZONE ");
      x.getValue().accept(this);
      return false;
   }

   public void endVisit(SQLServerDateTimeExpr x) {
   }

   public boolean visit(SQLServerOver x) {
      this.print0("(");
      if (x.getReferenceWindowName() != null) {
         x.getReferenceWindowName().accept(this);
         this.print(' ');
      }

      super.printSQLOVER(x);
      this.print(')');
      return false;
   }

   public void endVisit(SQLServerOver x) {
   }

   public boolean visit(SQLServerSelectSubqueryTableSource x) {
      this.print('(');
      ++this.indentCount;
      this.println();
      x.getSelect().accept(this);
      --this.indentCount;
      this.println();
      this.print(')');
      if (x.getAlias() != null && x.getAlias().length() != 0) {
         this.print0(this.ucase ? " AS " : " as ");
         this.print0(x.getAlias());
         if (x.getColumns().size() > 0) {
            if (x.getAlias() == null) {
               this.print0(this.ucase ? " AS" : " as");
            }

            this.print0(" (");
            this.printAndAccept(x.getColumns(), ", ");
            this.print(')');
         }
      }

      this.printPivot(x.getPivot(), x.getPivotAlias());
      return false;
   }

   public void endVisit(SQLServerSelectSubqueryTableSource x) {
   }

   public boolean visit(SQLServerValuesTableSource x) {
      List<SQLName> columns = x.getColumns();
      boolean quote = columns.size() > 0;
      if (quote) {
         this.print('(');
      }

      this.print0(this.ucase ? "VALUES " : "values ");
      this.printAndAccept(x.getValues(), ", ");
      if (quote) {
         this.print(')');
      }

      String alias = x.getAlias();
      if (alias != null) {
         this.print0(this.ucase ? " AS " : " as ");
         this.print0(alias);
      }

      if (columns.size() > 0) {
         if (alias == null) {
            this.print0(this.ucase ? " AS" : " as");
         }

         this.print0(" (");
         this.printAndAccept(columns, ", ");
         this.print(')');
      }

      this.printPivot(x.getPivot(), x.getPivotAlias());
      return false;
   }

   public void endVisit(SQLServerValuesTableSource x) {
   }

   public boolean visit(SQLServerSelectPivot x) {
      this.print0(this.ucase ? "PIVOT" : "pivot");
      if (x.isXml()) {
         this.print0(this.ucase ? " XML" : " xml");
      }

      this.print0(" (");
      this.printAndAccept(x.getItems(), ", ");
      if (x.getPivotFor().size() > 0) {
         this.print0(this.ucase ? " FOR " : " for ");
         if (x.getPivotFor().size() == 1) {
            ((SQLExpr)x.getPivotFor().get(0)).accept(this);
         } else {
            this.print('(');
            this.printAndAccept(x.getPivotFor(), ", ");
            this.print(')');
         }
      }

      if (x.getPivotIn().size() > 0) {
         this.print0(this.ucase ? " IN (" : " in (");
         this.printAndAccept(x.getPivotIn(), ", ");
         this.print(')');
      }

      this.print(')');
      return false;
   }

   public void endVisit(SQLServerSelectPivot x) {
   }

   public boolean visit(SQLServerSelectPivot.Item x) {
      x.getExpr().accept(this);
      if (x.getAlias() != null && x.getAlias().length() > 0) {
         this.print0(this.ucase ? " AS " : " as ");
         this.print0(x.getAlias());
      }

      return false;
   }

   public void endVisit(SQLServerSelectPivot.Item x) {
   }

   public boolean visit(SQLServerSelectUnPivot x) {
      this.print0(this.ucase ? "UNPIVOT" : "unpivot");
      this.print0(" (");
      if (x.getItems().size() == 1) {
         ((SQLExpr)x.getItems().get(0)).accept(this);
      } else {
         this.print0(" (");
         this.printAndAccept(x.getItems(), ", ");
         this.print(')');
      }

      if (x.getPivotFor().size() > 0) {
         this.print0(this.ucase ? " FOR " : " for ");
         if (x.getPivotFor().size() == 1) {
            ((SQLExpr)x.getPivotFor().get(0)).accept(this);
         } else {
            this.print('(');
            this.printAndAccept(x.getPivotFor(), ", ");
            this.print(')');
         }
      }

      if (x.getPivotIn().size() > 0) {
         this.print0(this.ucase ? " IN (" : " in (");
         this.printAndAccept(x.getPivotIn(), ", ");
         this.print(')');
      }

      this.print(')');
      return false;
   }

   public void endVisit(SQLServerSelectUnPivot x) {
   }

   public boolean visit(SQLServerUnionQueryTableSource x) {
      this.print('(');
      ++this.indentCount;
      this.println();
      x.getUnion().accept(this);
      --this.indentCount;
      this.println();
      this.print(')');
      List<SQLName> columns = x.getColumns();
      String alias = x.getAlias();
      if (alias != null) {
         this.print0(this.ucase ? " AS " : " as ");
         this.print0(alias);
      }

      if (columns.size() > 0) {
         this.print0(" (");

         for(int i = 0; i < columns.size(); ++i) {
            if (i != 0) {
               this.print0(", ");
            }

            this.printExpr((SQLExpr)columns.get(i));
         }

         this.print(')');
      }

      this.printPivot(x.getPivot(), x.getPivotAlias());
      return false;
   }

   public void endVisit(SQLServerUnionQueryTableSource x) {
   }

   private void printPivot(SQLServerPivotBase pivot, String pivotAlias) {
      if (pivot != null) {
         this.println();
         pivot.accept(this);
         if (pivotAlias != null) {
            this.print0(this.ucase ? " AS " : " as ");
            this.print0(pivotAlias);
         }
      }

   }

   public boolean visit(SQLServerCurrentOfCursorExpr x) {
      this.print0(this.ucase ? "CURRENT OF " : "current of ");
      if (x.isGlobal()) {
         this.print0(this.ucase ? "GLOBAL " : "global ");
      }

      this.printExpr(x.getCursorName(), this.parameterized);
      return false;
   }

   public void endVisit(SQLServerCurrentOfCursorExpr x) {
   }

   public boolean visit(SQLServerMergeStatement x) {
      SQLWithSubqueryClause with = x.getWith();
      if (with != null) {
         this.visit((SQLWithSubqueryClause)with);
         this.println();
      }

      this.print0(this.ucase ? "MERGE " : "merge ");
      if (x.getHints().size() > 0) {
         this.printAndAccept(x.getHints(), ", ");
         this.print(' ');
      }

      if (x.getTop() != null) {
         x.getTop().accept(this);
         this.print(' ');
      }

      this.print0(this.ucase ? "INTO " : "into ");
      x.getInto().accept(this);
      this.println();
      this.print0(this.ucase ? "USING " : "using ");
      x.getUsing().accept(this);
      this.print0(this.ucase ? " ON (" : " on (");
      x.getOn().accept(this);
      this.print0(") ");

      for(SQLObjectImpl object : x.getClauses()) {
         this.println();
         object.accept(this);
      }

      if (x.getOutput() != null) {
         this.println();
         x.getOutput().accept(this);
      }

      return false;
   }

   public void endVisit(SQLServerMergeStatement x) {
   }

   public boolean visit(SQLServerMergeStatement.MergeDeleteClause x) {
      if (x.isBySourse()) {
         this.print0(this.ucase ? "WHEN NOT MATCHED BY SOURCE " : "when NOT matched BY SOURCE ");
      } else {
         this.print0(this.ucase ? "WHEN MATCHED " : "when matched ");
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.print0(this.ucase ? "AND " : "and ");
         this.printExpr(where, this.parameterized);
         this.print0(" ");
      }

      this.print0(this.ucase ? "THEN DELETE " : "then delete ");
      return false;
   }

   public void endVisit(SQLServerMergeStatement.MergeDeleteClause x) {
   }

   public boolean visit(SQLServerMergeStatement.SQLServerMergeUpdateClause x) {
      if (x.isBySourse()) {
         this.print0(this.ucase ? "WHEN NOT MATCHED BY SOURCE " : "when NOT matched BY SOURCE ");
      } else {
         this.print0(this.ucase ? "WHEN MATCHED " : "when matched ");
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.print0(this.ucase ? "AND " : "and ");
         this.printExpr(where, this.parameterized);
         this.print0(" ");
      }

      this.print0(this.ucase ? "THEN UPDATE SET " : "then update set ");
      this.printAndAccept(x.getItems(), ", ");
      return false;
   }

   public void endVisit(SQLServerMergeStatement.SQLServerMergeUpdateClause x) {
   }

   public boolean visit(SQLServerMergeStatement.SQLServerMergeInsertClause x) {
      this.print0(this.ucase ? "WHEN NOT MATCHED " : "when not matched ");
      if (x.isByTarget()) {
         this.print0(this.ucase ? "BY TARGET " : "by target ");
      }

      SQLExpr where = x.getWhere();
      if (where != null) {
         this.print0(this.ucase ? "AND " : "and ");
         this.printExpr(where, this.parameterized);
         this.print0(" ");
      }

      this.print0(this.ucase ? "THEN INSERT " : "then insert ");
      if (x.getColumns().size() > 0) {
         this.print(" (");
         this.printAndAccept(x.getColumns(), ", ");
         this.print(')');
      }

      if (x.isDefaultValues()) {
         this.print0(this.ucase ? " DEFAULT  VALUES " : " default values ");
      } else {
         this.print0(this.ucase ? " VALUES (" : " values (");
         this.printAndAccept(x.getValues(), ", ");
         this.print(')');
      }

      return false;
   }

   public void endVisit(SQLServerMergeStatement.SQLServerMergeInsertClause x) {
   }
}
