001package gudusoft.gsqlparser.nodes;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.scriptWriter.TScriptGenerator;
005import gudusoft.gsqlparser.sqlenv.TSQLEnv;
006import gudusoft.gsqlparser.util.gspMD5Utils;
007
008import java.util.ArrayList;
009import java.util.Iterator;
010
011import static java.nio.charset.StandardCharsets.UTF_8;
012
013/**
014 * <h1>TParseTreeNode - Core Parse Tree Node Implementation</h1>
015 * 
016 * <h2>Overview</h2>
017 * This class is the root class for all syntax/parse tree nodes in the GSP SQL Parser.
018 * A parse tree node represents a syntactic element of a SQL statement, composed of a sequence 
019 * of tokens generated by the lexer. The node maintains references to its start and end tokens
020 * in a double-linked token chain, enabling both traversal and modification of the SQL text.
021 *
022 * <h2>Architecture and Design</h2>
023 * 
024 * <h3>Token Chain Structure</h3>
025 * The parse tree is built on a double-linked list of {@link TSourceToken} objects. Each node:
026 * <ul>
027 *   <li>Has a {@code startToken} and {@code endToken} that define its boundaries</li>
028 *   <li>Can iterate through its tokens using the token chain links</li>
029 *   <li>Maintains relationships with other nodes that may share the same tokens</li>
030 * </ul>
031 * 
032 * <h3>Critical: Node Nesting and Token Sharing</h3>
033 * <b>Nodes in the parse tree are nested and share tokens.</b> This is fundamental to the design:
034 * <ul>
035 *   <li>A WHERE clause node contains an expression node, both sharing the same tokens</li>
036 *   <li>A SELECT statement contains multiple clauses, all sharing tokens from the same chain</li>
037 *   <li>Each token maintains stacks of nodes that start/end on it (via {@code nodesStartFromThisToken} 
038 *       and {@code nodesEndWithThisToken})</li>
039 * </ul>
040 * 
041 * <h4>Token Sharing Example:</h4>
042 * <pre>
043 * SQL: "SELECT * FROM table WHERE id = 1"
044 * 
045 * - SelectStatement node: [SELECT...1]
046 *   - SelectList node: [*]
047 *   - FromClause node: [FROM table]
048 *   - WhereClause node: [WHERE id = 1]
049 *     - Expression node: [id = 1]
050 *       - LeftOperand: [id]
051 *       - Operator: [=]
052 *       - RightOperand: [1]
053 * </pre>
054 * 
055 * <h3>Modification Strategy for Nested Nodes</h3>
056 * 
057 * <h4>Core Rules:</h4>
058 * <ol>
059 *   <li><b>Leaf-First Modification:</b> Always modify the most specific (leaf) node when possible</li>
060 *   <li><b>Status Propagation:</b> When a node is modified, all overlapping nodes are updated:
061 *       <ul>
062 *         <li>{@code nsDetached}: Node's tokens were partially modified</li>
063 *         <li>{@code nsPartitial}: Node contains modified sub-nodes</li>
064 *         <li>{@code nsRemoved}: Node's tokens were completely removed</li>
065 *       </ul>
066 *   </li>
067 *   <li><b>Chain Integrity:</b> The token chain must remain valid after any modification</li>
068 * </ol>
069 * 
070 * <h4>Modification Scenarios and Handling:</h4>
071 * 
072 * <b>Scenario 1: Modifying a Leaf Node</b>
073 * <pre>
074 * // Original: WHERE id = 1
075 * // Modify the right operand from "1" to "100"
076 * rightOperandNode.setString("100");
077 * 
078 * Result:
079 * - RightOperand node: replaced with new token "100"
080 * - Expression node: status = nsPartitial (contains modified child)
081 * - WhereClause node: status = nsPartitial (contains modified descendant)
082 * </pre>
083 * 
084 * <b>Scenario 2: Modifying a Parent Node</b>
085 * <pre>
086 * // Original: WHERE id = 1
087 * // Replace entire WHERE clause
088 * whereClauseNode.setString("WHERE status = 'active'");
089 * 
090 * Result:
091 * - All child nodes (Expression, operands): status = nsDetached
092 * - WhereClause node: completely replaced with new tokens
093 * - SelectStatement: updated to reference new WHERE tokens
094 * </pre>
095 * 
096 * <b>Scenario 3: Removing a Node</b>
097 * <pre>
098 * // Remove WHERE clause entirely
099 * whereClauseNode.removeTokens();
100 * 
101 * Result:
102 * - WhereClause and all children: status = nsRemoved
103 * - Tokens removed from chain
104 * - Parent SelectStatement: adjusted boundaries
105 * </pre>
106 * 
107 * <h3>Best Practices for API Users</h3>
108 * 
109 * <ol>
110 *   <li><b>Check Node Status Before Modification:</b>
111 *       <pre>
112 *       if (node.getNodeStatus() == ENodeStatus.nsNormal) {
113 *           // Safe to modify
114 *           node.setString(newText);
115 *       }
116 *       </pre>
117 *   </li>
118 *   
119 *   <li><b>Prefer Leaf Node Modifications:</b>
120 *       <pre>
121 *       // GOOD: Modify specific value
122 *       tableNameNode.setString("new_table");
123 *       
124 *       // AVOID: Replacing entire FROM clause when only table name needs change
125 *       fromClause.setString("FROM new_table");
126 *       </pre>
127 *   </li>
128 *   
129 *   <li><b>Use toScript() for Complex Modifications:</b>
130 *       <pre>
131 *       // After multiple modifications, use script generator
132 *       if (statement.isChanged()) {
133 *           String result = statement.toScript();
134 *       }
135 *       </pre>
136 *   </li>
137 *   
138 *   <li><b>Understand Cascading Effects:</b>
139 *       <pre>
140 *       // Modifying a node affects all overlapping nodes
141 *       expression.setString("new_condition");
142 *       // Parent WHERE clause is now marked as modified
143 *       assert whereClause.getNodeStatus() == ENodeStatus.nsPartitial;
144 *       </pre>
145 *   </li>
146 * </ol>
147 * 
148 * <h3>Text Modification Mechanism</h3>
149 * The node supports dynamic text modification through the {@link #setString(String)} method:
150 * <ol>
151 *   <li><b>Tokenization:</b> New text is tokenized using {@link TSingletonParser} to create new tokens</li>
152 *   <li><b>Chain Replacement:</b> Old tokens are removed from the chain and replaced with new ones</li>
153 *   <li><b>Relationship Updates:</b> All nodes sharing the modified tokens are updated:
154 *       <ul>
155 *         <li>{@code updateNodeWithTheSameStartToken()}: Updates nodes starting at modified position</li>
156 *         <li>{@code updateMeNodeWithTheSameEndToken()}: Updates nodes ending at modified position</li>
157 *         <li>{@code updateStatusOfNodeShareSameTokens()}: Propagates status changes</li>
158 *       </ul>
159 *   </li>
160 *   <li><b>Status Tracking:</b> Node status changes to track modifications</li>
161 * </ol>
162 * 
163 * <h3>String Reconstruction ({@link #toString()})</h3>
164 * The toString() method uses a sophisticated two-pass algorithm when {@code doubleLinkedTokenListToString} is true:
165 * <ul>
166 *   <li><b>First Pass:</b> Detects chain modifications and marks redundant newlines for deletion</li>
167 *   <li><b>Second Pass:</b> Builds the final string by concatenating non-deleted tokens</li>
168 * </ul>
169 * This approach cleanly handles both original parsed text and user modifications.
170 *
171 * <h2>Current Implementation Trade-offs</h2>
172 * 
173 * <h3>Design Decisions (Simplicity over Features)</h3>
174 * <ul>
175 *   <li><b>Re-tokenization on Every Modification:</b> Simple but potentially expensive. 
176 *       Accepted trade-off for reliability and consistency.</li>
177 *   <li><b>No Partial Token Modification:</b> Cannot modify part of a token; must replace entire token. 
178 *       Keeps token integrity and chain management simple.</li>
179 *   <li><b>Status-based Conflict Resolution:</b> When nodes overlap, status flags determine behavior. 
180 *       Simple rule-based system rather than complex conflict resolution.</li>
181 *   <li><b>Single Token Chain:</b> All nodes reference the same global chain. 
182 *       Simpler than managing multiple chains but requires careful synchronization.</li>
183 * </ul>
184 * 
185 * <h3>Limitations by Design</h3>
186 * <ul>
187 *   <li><b>Cannot Preserve Comments in Modified Nodes:</b> When a node is replaced via setString(), 
188 *       internal comments are lost. This is acceptable for most use cases.</li>
189 *   <li><b>No Undo/Redo:</b> Modifications are permanent. Applications must manage their own undo stack.</li>
190 *   <li><b>Thread Safety:</b> Not thread-safe by design. Simpler implementation, caller must synchronize.</li>
191 *   <li><b>Memory vs Speed:</b> Keeps all tokens in memory for fast access. Not optimized for very large SQLs.</li>
192 * </ul>
193 *
194 * <h2>Implementation Details</h2>
195 * 
196 * <h3>Token Sharing Management</h3>
197 * When modifying a node with shared tokens:
198 * <ol>
199 *   <li>Calculate token count for all affected nodes ({@code refreshAllNodesTokenCount()})</li>
200 *   <li>Identify nodes with same start/end tokens</li>
201 *   <li>Update node boundaries based on token count comparison:
202 *       <ul>
203 *         <li>Same count = sibling node, needs same update</li>
204 *         <li>Larger count = parent node, needs boundary adjustment</li>
205 *         <li>Smaller count = child node, may become detached</li>
206 *       </ul>
207 *   </li>
208 *   <li>Splice new tokens into chain maintaining prev/next links</li>
209 * </ol>
210 * 
211 * <h3>Status Flags and Their Meanings</h3>
212 * <ul>
213 *   <li>{@code nsNormal}: Unmodified node with original tokens</li>
214 *   <li>{@code nsFlattened}: Node created from user text (no original tokens)</li>
215 *   <li>{@code nsPartitial}: Contains modified child nodes but own tokens unchanged</li>
216 *   <li>{@code nsDetached}: Tokens modified by parent/sibling operation</li>
217 *   <li>{@code nsRemoved}: Tokens removed from chain, node is defunct</li>
218 * </ul>
219 *
220 * <h2>Proposed Future Improvements</h2>
221 * 
222 * <h3>1. Decouple Lexer Reference</h3>
223 * <pre>
224 * // Instead of token->lexer reference, use a TokenContext:
225 * class TokenContext {
226 *     EDbVendor vendor;
227 *     SourceBuffer sourceBuffer;
228 *     int sourceId; // Identifies original vs modified source
229 * }
230 * </pre>
231 * 
232 * <h3>2. Implement SourceBuffer System</h3>
233 * <pre>
234 * class SourceBuffer {
235 *     List&lt;TextSegment&gt; segments;
236 *     
237 *     class TextSegment {
238 *         String text;
239 *         SourceType type; // ORIGINAL, USER_MODIFIED, GENERATED
240 *         int startOffset;
241 *         int endOffset;
242 *     }
243 *     
244 *     String getText(int segmentId, int start, int end);
245 *     int addSegment(String text, SourceType type);
246 * }
247 * </pre>
248 * 
249 * <h3>3. Token Source Tracking</h3>
250 * <pre>
251 * class TSourceToken {
252 *     int sourceBufferId;
253 *     int segmentId;
254 *     int startOffset;
255 *     int endOffset;
256 *     SourceType sourceType;
257 *     
258 *     String getText() {
259 *         return SourceBufferManager.getBuffer(sourceBufferId)
260 *                                  .getText(segmentId, startOffset, endOffset);
261 *     }
262 * }
263 * </pre>
264 * 
265 * <h3>4. Lazy Token Creation</h3>
266 * <ul>
267 *   <li>Don't re-tokenize for simple text replacements</li>
268 *   <li>Create synthetic tokens only when necessary</li>
269 *   <li>Reuse existing tokens when possible</li>
270 * </ul>
271 * 
272 * <h3>5. Unified Modification API</h3>
273 * <pre>
274 * class NodeModification {
275 *     ModificationType type; // REPLACE, INSERT, DELETE
276 *     String newText;
277 *     boolean preserveFormatting;
278 *     boolean retokenize;
279 * }
280 * 
281 * public void modifyNode(NodeModification mod) {
282 *     // Single entry point for all modifications
283 * }
284 * </pre>
285 *
286 * <h2>Critical Methods Documentation</h2>
287 * 
288 * <h3>{@link #setString(String)}</h3>
289 * Main entry point for modifying node text. Process:
290 * <ol>
291 *   <li>Tokenizes new text using TSingletonParser</li>
292 *   <li>Creates new token chain from tokenized text</li>
293 *   <li>Replaces old tokens in the chain</li>
294 *   <li>Updates all affected node relationships</li>
295 * </ol>
296 * 
297 * <h3>{@link #toString()}</h3>
298 * Reconstructs text from token chain. Two-pass algorithm:
299 * <ol>
300 *   <li><b>Pass 1:</b> Detect modifications and mark redundant formatting</li>
301 *   <li><b>Pass 2:</b> Build string from valid tokens</li>
302 * </ol>
303 * 
304 * <h3>{@link #setText(TSourceToken, TSourceToken)}</h3>
305 * Low-level token replacement. Core of the modification system:
306 * <ul>
307 *   <li>Updates token chain links</li>
308 *   <li>Maintains node relationships</li>
309 *   <li>Handles edge cases (null tokens, chain boundaries)</li>
310 * </ul>
311 *
312 * <h2>Usage Patterns</h2>
313 * 
314 * <h3>Reading Original Text</h3>
315 * <pre>
316 * TParseTreeNode node = ...;
317 * String originalText = node.toString(); // Get text from token chain
318 * </pre>
319 * 
320 * <h3>Modifying Node Text</h3>
321 * <pre>
322 * node.setString("new SQL text"); // Replace node's text
323 * String modifiedText = node.toString(); // Get modified text
324 * </pre>
325 * 
326 * <h3>Checking Modifications</h3>
327 * <pre>
328 * if (node.isChanged()) {
329 *     String newText = node.toScript(); // Use script generator for complex mods
330 * }
331 * </pre>
332 *
333 * <h2>Thread Safety</h2>
334 * This class is NOT thread-safe. Concurrent modifications to the token chain
335 * or node structure may result in inconsistent state or corruption.
336 *
337 * <h2>Performance Considerations</h2>
338 * <ul>
339 *   <li>Token chain traversal is O(n) where n is number of tokens</li>
340 *   <li>Text modification requires re-tokenization (expensive)</li>
341 *   <li>toString() performs two passes over tokens</li>
342 *   <li>Node relationship updates can cascade to many nodes</li>
343 * </ul>
344 *
345 * @see TSourceToken
346 * @see TSingletonParser
347 * @see TScriptGenerator
348 * @since 1.0
349 */
350public abstract class TParseTreeNode implements Visitable,Iterator<TSourceToken>{
351
352    public void setEvaluateDatatype(Class<?> evaluateDatatype) {
353        this.evaluateDatatype = evaluateDatatype;
354    }
355
356    /**
357     *  Support String, Integer, Double and Boolean type
358     *
359     * @return
360     */
361    public Class<?> getEvaluateDatatype() {
362        return evaluateDatatype;
363    }
364
365    private Class<?> evaluateDatatype;
366    private Object evalValue;
367
368    public void setEvalValue(Object evalValue) {
369        this.evalValue = evalValue;
370    }
371
372    public Object getEvalValue() {
373        return evalValue;
374    }
375
376    private String plainText = null;
377
378    public void setPlainText(String plainText) {
379        this.plainText = plainText;
380    }
381
382    public String getPlainText() {
383        if (plainText == null){
384            return toString();
385        }else{
386            return plainText;
387        }
388    }
389
390    private ESqlClause location = ESqlClause.unknown;
391
392    public void setLocation(ESqlClause location) {
393        this.location = location;
394    }
395
396    /**
397     * SQL clause that include this objectName such as select list, from clause, set clause
398     *
399     * @return SQL clause that include this objectName
400     */
401    public ESqlClause getLocation() {
402        return location;
403    }
404
405    private String compactString = null;
406
407    /**
408     * representation of this node by removing all spaces, return and comments
409     * @return string representation of this node by removing all spaces, return and comments
410     */
411    public String getCompactString(){
412        if (compactString != null) return compactString;
413
414        StringBuilder sb = new StringBuilder();
415        TSourceToken st = null;
416        currentIterateToken = null;
417        while (this.hasNext()){
418            st = next();
419            if ((st.tokencode == TBaseType.lexspace)||(st.tokencode == TBaseType.lexnewline)
420                    ||(st.tokencode == TBaseType.cmtslashstar)||(st.tokencode == TBaseType.cmtdoublehyphen)
421            ) {
422                continue;
423            }else{
424                sb.append(st.toString());
425            }
426        }
427
428        compactString = sb.toString();
429        return compactString;
430    }
431
432    private String md5 = null;
433    /**
434     *  md5 hash code of the string representation of this node.
435     *  If the string of this node is empty, return the md5 value of the class name of this node.
436     *
437     *  1. remove all spaces, return, comments inside the node<br>
438     *  2. turn number constant in where clause into ? character. <br>
439     *  3. turn string constant in where clause into ?? character. <br>
440     *  4. turn all string of the token into uppercase if it's not a delimited identifier.
441     *
442     * @return md5 hash code of the string representation of this node
443     */
444    public String getMd5(){
445        if (md5 != null) return md5;
446
447        StringBuilder sb = new StringBuilder();
448        TSourceToken st = null;
449        currentIterateToken = null;
450        while (this.hasNext()){
451            st = next();
452            // cmtslashstar lexspace lexnewline cmtdoublehyphen
453            if ((st.tokencode == TBaseType.lexspace)||(st.tokencode == TBaseType.lexnewline)
454                    ||(st.tokencode == TBaseType.cmtslashstar)||(st.tokencode == TBaseType.cmtdoublehyphen)
455            ){
456                continue;
457            } else if ((st.tokencode == TBaseType.fconst)||(st.tokencode == TBaseType.iconst)){
458                if (st.location == ESqlClause.where){
459                    sb.append("?");
460                }else{
461                    sb.append(st.toString());
462                }
463            } else if (st.tokencode == TBaseType.sconst){
464                if (st.location == ESqlClause.where){
465                    sb.append("??");
466                }else{
467                    sb.append(st.toString());
468                }
469            } else if (TSQLEnv.isDelimitedIdentifier(this.dbvendor,st.toString())){
470                sb.append(st.toString());
471            }else {
472                sb.append(st.toString().toUpperCase());
473            }
474        }
475        byte[] md5InBytes;
476        if (sb.toString().length() > 0){
477            md5InBytes = gspMD5Utils.digest(sb.toString().getBytes(UTF_8));
478        }else{
479            md5InBytes = gspMD5Utils.digest(this.getClass().getName() .getBytes(UTF_8));
480        }
481        md5 = gspMD5Utils.bytesToHex(md5InBytes);
482        return md5;
483
484    }
485
486    public TParseTreeNode getParentObjectName() {
487        return parent;
488    }
489
490    public void setParent(TParseTreeNode parent) {
491        this.parent = parent;
492    }
493
494    private TParseTreeNode parent = null;
495
496//    public void  appendAToken(TSourceToken st){
497//        // this token is not belonged to this node
498//        this.getEndToken()
499//    }
500    public static boolean doubleLinkedTokenListToString = true;
501
502//    TSourceToken st = getStartToken();
503//    TSourceToken et = getEndToken();
504    public void insertAfterAToken(TSourceToken anchorToken){
505        if ((anchorToken == null)||(this.getEndToken() == null)||(this.getStartToken() == null) )return;
506        this.getEndToken().setNextTokenInChain(anchorToken.getNextTokenInChain());
507        if (anchorToken.getNextTokenInChain() != null){
508            anchorToken.getNextTokenInChain().setPrevTokenInChain(this.getEndToken());
509        }else{
510            if (anchorToken.getNodesEndWithThisToken().size() > 0){
511                for(Object o:anchorToken.getNodesEndWithThisToken()){
512                    TParseTreeNode node = (TParseTreeNode)o;
513                    node.setEndToken(this.getEndToken());
514                }
515            }
516        }
517        anchorToken.setNextTokenInChain(this.getStartToken());
518        this.getStartToken().setPrevTokenInChain(anchorToken);
519
520    }
521
522   public void insertNewNodeBeforeMe(TParseTreeNode newNode, boolean needCommaBefore){
523
524       updateNodeWithTheSameStartToken(nodeActionInsert,newNode.getStartToken());
525
526       newNode.getStartToken().setPrevTokenInChain(this.getStartToken().getPrevTokenInChain());
527       if (this.getStartToken().getPrevTokenInChain() != null){
528           this.getStartToken().getPrevTokenInChain().setNextTokenInChain(newNode.getStartToken());
529       }
530
531       newNode.getEndToken().setNextTokenInChain(this.getStartToken());
532       this.getStartToken().setPrevTokenInChain(newNode.getEndToken());
533
534       if (needCommaBefore) {
535           TSourceToken commaToken = new TSourceToken(",");
536           newNode.getEndToken().insertANewTokenAfterMe(commaToken);
537       }
538
539   }
540
541   public void appendNewNode(TParseTreeNode newNode, boolean needCommaBefore){
542        // the new node must be share the same parent node with this node
543
544       refreshAllNodesTokenCount();
545       updateMeNodeWithTheSameEndToken(nodeActionAppend,newNode.getEndToken());
546
547       newNode.getEndToken().setNextTokenInChain(this.getEndToken().getNextTokenInChain());
548       if (this.getEndToken().getNextTokenInChain() != null){
549           this.getEndToken().getNextTokenInChain().setPrevTokenInChain(newNode.getEndToken());
550       }
551
552       newNode.getStartToken().setPrevTokenInChain(this.getEndToken());
553       this.getEndToken().setNextTokenInChain(newNode.getStartToken());
554
555       if (needCommaBefore) {
556           TSourceToken commaToken = new TSourceToken(",");
557           this.getEndToken().insertANewTokenAfterMe(commaToken);
558       }
559
560   }
561
562   public void replaceWithNewNode(TParseTreeNode newNode){
563       if ((getStartToken() == null)||(getEndToken() == null)) return;
564       if ((newNode.getStartToken() == null)||(newNode.getEndToken() == null)) return;
565
566       if (getStartToken().getPrevTokenInChain() == null) return;
567
568       refreshAllNodesTokenCount();
569       updateNodeWithTheSameStartToken(nodeActionUpdate,newNode.startToken);
570       updateMeNodeWithTheSameEndToken(nodeActionUpdate,newNode.endToken);
571
572       getStartToken().getPrevTokenInChain().setNextTokenInChain(newNode.getStartToken());
573       newNode.getStartToken().setPrevTokenInChain(getStartToken().getPrevTokenInChain());
574
575       newNode.getEndToken().setNextTokenInChain(getEndToken().getNextTokenInChain());
576       if (getEndToken().getNextTokenInChain() != null){
577           getEndToken().getNextTokenInChain().setPrevTokenInChain(newNode.getEndToken());
578       }
579
580   }
581
582
583
584    TSourceToken currentIterateToken = null;
585
586
587    public void resetIterator(){
588        currentIterateToken = null;
589    }
590
591    @Override
592    public boolean hasNext() {
593        if ((getStartToken() != null)&&(getEndToken() != null)&&(currentIterateToken != getEndToken())){
594            return true;
595        }
596        else{
597            return false;
598        }
599
600    }
601
602    @Override
603    public TSourceToken next() {
604        if (this.hasNext()){
605            if (currentIterateToken == null){
606                currentIterateToken = getStartToken();
607            }else {
608                currentIterateToken = currentIterateToken.getNextTokenInChain();
609            }
610
611            return currentIterateToken;
612        }
613        else
614            return null;
615    }
616
617    @Override
618    public void remove() {
619        throw new UnsupportedOperationException();
620    }
621
622//    @Override
623//    public Iterator<TSourceToken> iterator() {
624//        return new TokenSetIterator();
625//    }
626//
627//    private class TokenSetIterator implements Iterator {
628//        TSourceToken st = getStartToken();
629//        TSourceToken et = getEndToken();
630//        TSourceToken currentToken = null;
631//
632//         public boolean hasNext() {
633//            if ((st != null)&&(et != null)&&(currentToken != et)){
634//                return true;
635//            }
636//            else
637//                return false;
638//        }
639//
640//        public TSourceToken next() {
641//            if (this.hasNext()){
642//                if (currentToken == null){
643//                    currentToken = st;
644//                }else {
645//                    currentToken = currentToken.getNextTokenInChain();
646//                }
647//
648//                return currentToken;
649//            }
650//            else
651//                return null;
652//        }
653//
654//        @Override
655//        public void remove() {
656//
657//        }
658//    }
659
660    public void setNodeStatus(ENodeStatus nodeStatus) {
661        this.nodeStatus = nodeStatus;
662    }
663
664    public ENodeStatus getNodeStatus() {
665
666        return nodeStatus;
667    }
668
669    private ENodeStatus nodeStatus = ENodeStatus.nsNormal;
670
671
672
673    public void addToTokenChain(TSourceToken anchorToken, boolean beforeAnchorToken){
674
675    };
676
677    private int tokenCount = -1;
678
679    public int getTokenCount() {
680        if (tokenCount == -1){
681            calculateTokenCount();
682        }
683        return tokenCount;
684    }
685
686    public void refreshAllNodesTokenCount(){
687        if (this.startToken != null){
688            for(int i=0;i<this.startToken.getNodesStartFromThisToken().size();i++){
689                TParseTreeNode node = this.startToken.getNodesStartFromThisToken().get(i);
690                node.calculateTokenCount();
691            }
692        }
693
694        if (this.endToken != null){
695            for(int i=0;i<this.endToken.getNodesEndWithThisToken().size();i++){
696                TParseTreeNode node = this.endToken.getNodesEndWithThisToken().get(i);
697                node.calculateTokenCount();
698            }
699        }
700
701    }
702
703    public void calculateTokenCount(){
704        int ret = 0;
705        TSourceToken prevst = null;
706        this.resetIterator();
707        while (this.hasNext()){
708            ret++;
709            TSourceToken st = this.next();
710            if (st == null){
711                if (TBaseType.DEBUG){
712                    System.out.println("==========ERROR: start and token is not in the same chain  ===============\n");
713                    System.out.println("Start Token:"+this.getStartToken().toString());
714                    System.out.println("End Token:"+this.getEndToken().toString());
715                    if (prevst != null){
716                        System.out.println("Prev token, code:"+prevst.tokencode+", Text:"+prevst.toString());
717                    }else{
718                        System.out.println("Prev token is null");
719                    }
720                    System.out.println("============================\n");
721                }
722                break;
723            }
724            prevst = st;
725        }
726        this.resetIterator();
727        this.tokenCount = ret;
728    }
729
730    public final static int nodeActionUnknown = 0;
731    public final static int nodeActionRemove = 1;
732    public final static int nodeActionInsert = 2;
733    public final static int nodeActionUpdate = 3;
734    public final static int nodeActionUpdateText = 4;
735    public final static int nodeActionAppend = 5;
736    public final static int nodeChangeStartToken = 6;
737    public final static int nodeChangeEndToken = 7;
738
739    void updateStatusOfNodeShareSameTokens(int nodeAction){
740        TSourceToken startToken = getStartToken();
741        TSourceToken endToken = getEndToken();
742        if (endToken == null) return;
743        if (startToken == null) return;
744
745        // System.out.println("node token count:"+thisNodeTokenCount);
746        this.resetIterator();
747        while(this.hasNext()){
748            TSourceToken st = this.next();
749           // System.out.println(st.toString());
750            if (st == null) break;
751
752            for(int i=0;i<st.getNodesStartFromThisToken().size();i++){
753                TParseTreeNode node = st.getNodesStartFromThisToken().get(i);
754                if (st == startToken){
755                    if (this == node){
756                        switch (nodeAction){
757                            case nodeActionRemove:
758                                node.nodeStatus = ENodeStatus.nsRemoved;
759                                break;
760                            default:
761                                node.nodeStatus = ENodeStatus.nsDetached;
762                                break;
763                        }
764                    }else if (node.getTokenCount() == this.getTokenCount()){
765                        switch (nodeAction){
766                            case nodeActionRemove:
767                                node.nodeStatus = ENodeStatus.nsRemoved;
768                                break;
769                            default:
770                                node.nodeStatus = ENodeStatus.nsDetached;
771                                break;
772                        }
773                    }else if (node.getTokenCount() > this.getTokenCount()){
774                        node.nodeStatus = ENodeStatus.nsPartitial;
775                    }else{
776                        node.nodeStatus = ENodeStatus.nsDetached;
777                    }
778                }else{
779                    node.nodeStatus = ENodeStatus.nsDetached;
780                }
781            }
782
783            for(int i=0;i<st.getNodesEndWithThisToken().size();i++){
784                TParseTreeNode node = st.getNodesEndWithThisToken().get(i);
785                if (st == endToken){
786                    if (this == node){
787                        switch (nodeAction){
788                            case nodeActionRemove:
789                                node.nodeStatus = ENodeStatus.nsRemoved;
790                                break;
791                            default:
792                                node.nodeStatus = ENodeStatus.nsDetached;
793                                break;
794                        }
795                    }else if (node.getTokenCount() == this.getTokenCount()){
796                        switch (nodeAction){
797                            case nodeActionRemove:
798                                node.nodeStatus = ENodeStatus.nsRemoved;
799                                break;
800                            default:
801                                node.nodeStatus = ENodeStatus.nsDetached;
802                                break;
803                        }
804                    }else if (node.getTokenCount() > this.getTokenCount()){
805                        node.nodeStatus = ENodeStatus.nsPartitial;
806                    }else{
807                        switch (nodeAction){
808                            case nodeActionRemove:
809                                node.nodeStatus = ENodeStatus.nsRemoved;
810                                break;
811                            default:
812                                node.nodeStatus = ENodeStatus.nsDetached;
813                                break;
814                        }
815                    }
816                }else{
817                    switch (nodeAction){
818                        case nodeActionRemove:
819                            node.nodeStatus = ENodeStatus.nsRemoved;
820                            break;
821                        default:
822                            node.nodeStatus = ENodeStatus.nsDetached;
823                            break;
824                    }
825                }
826            }
827
828        }
829        this.resetIterator();
830    }
831
832    void updateNodeWithTheSameStartToken(int nodeAction, TSourceToken newStartToken){
833        TSourceToken oldStartToken = this.getStartToken();
834        for(int i=0;i<oldStartToken.getNodesStartFromThisToken().size();i++){
835            TParseTreeNode node = oldStartToken.getNodesStartFromThisToken().get(i);
836            if (node == this){ // myself
837                switch (nodeAction){
838                    case nodeActionRemove:
839                        node.startToken = null;
840                        break;
841                    case nodeActionUpdate:
842                    case nodeActionUpdateText:
843                    case nodeChangeStartToken:
844                        node.startToken = newStartToken;
845                        newStartToken.getNodesStartFromThisToken().push(node);
846                    default:
847                        break;
848                }
849            }else if((node.getTokenCount() == this.getTokenCount())){ // node with same tokens
850                switch (nodeAction){
851                    case nodeActionRemove:
852                        node.startToken = null;
853                        break;
854                    case nodeActionUpdate:
855                    case nodeActionUpdateText:
856                    case nodeChangeStartToken:
857                        node.startToken = newStartToken;
858                        newStartToken.getNodesStartFromThisToken().push(node);
859                    default:
860                        break;
861                }
862            }
863            else if((node.getTokenCount() > this.getTokenCount())) { // parent node
864                node.startToken = newStartToken;
865                newStartToken.getNodesStartFromThisToken().push(node);
866            }else{ // sub node
867
868            }
869        }
870    }
871
872    void updateMeNodeWithTheSameEndToken(int nodeAction, TSourceToken newEndToken){
873        TSourceToken oldEndToken = this.getEndToken();
874        for(int i=0;i<oldEndToken.getNodesEndWithThisToken().size();i++){
875            TParseTreeNode node = oldEndToken.getNodesEndWithThisToken().get(i);
876            if (node == this){ // myself
877                switch (nodeAction){
878                    case nodeActionRemove:
879                        node.endToken = null;
880                        break;
881                    case nodeActionUpdate:
882                    case nodeActionUpdateText:
883                    case nodeChangeEndToken:
884                        node.endToken = newEndToken;
885                        newEndToken.getNodesEndWithThisToken().push(node);
886                    default:
887                        break;
888                }
889            }else if((node.getTokenCount() == this.getTokenCount())){ // node with same tokens
890                switch (nodeAction){
891                    case nodeActionRemove:
892                        node.endToken = null;
893                        break;
894                    case nodeActionUpdate:
895                    case nodeActionUpdateText:
896                    case nodeChangeEndToken:
897                        node.endToken = newEndToken;
898                        newEndToken.getNodesEndWithThisToken().push(node);
899                    default:
900                        break;
901                }
902            }
903            else if((node.getTokenCount() > this.getTokenCount())) { // parent node
904                node.endToken = newEndToken;
905                newEndToken.getNodesEndWithThisToken().push(node);
906            }else{ // sub node
907
908            }
909        }
910    }
911
912    /**
913     * both begin and end token will be removed from the chain
914     *
915     * @param startToken
916     * @param endToken
917     */
918    public static void removeTokensBetweenToken(TSourceToken startToken, TSourceToken endToken){
919       if ((startToken == null)||(endToken == null)) return;
920        startToken.removeFromChain();
921        while (startToken != endToken){
922            startToken = startToken.getNextTokenInChain();
923            if (startToken == null) break;
924            startToken.removeFromChain();
925        }
926    }
927
928    public static void removeTokensBetweenNodes(TParseTreeNode startNode, TParseTreeNode endNode){
929        if ((startNode == null)||(endNode == null)) return;
930        if ((startNode.getEndToken() == null)||(endNode.getStartToken() == null)) return;
931        TSourceToken startToken = startNode.getEndToken().getNextTokenInChain();
932        TSourceToken endToken  = endNode.getStartToken().getPrevTokenInChain();
933        if ((startToken == null)||(endToken == null)) return;
934
935        startToken.removeFromChain();
936        while (startToken != endToken){
937            startToken = startToken.getNextTokenInChain();
938            if (startToken == null) break;
939            startToken.removeFromChain();
940        }
941    }
942
943    public void removeTokens(){
944        if (this.nodeStatus == ENodeStatus.nsRemoved) return;
945
946        TSourceToken oldStartToken = getStartToken();
947        TSourceToken oldEndToken = getEndToken();
948        if (oldEndToken == null) return;
949        if (oldStartToken == null) return;
950
951        refreshAllNodesTokenCount();
952        updateStatusOfNodeShareSameTokens(nodeActionRemove);
953
954        if (oldStartToken.getPrevTokenInChain() != null){
955            updateMeNodeWithTheSameEndToken(nodeActionRemove,oldStartToken.getPrevTokenInChain());
956            oldStartToken.getPrevTokenInChain().setNextTokenInChain(oldEndToken.getNextTokenInChain());
957        }
958        if (oldEndToken.getNextTokenInChain() != null){
959            updateNodeWithTheSameStartToken(nodeActionRemove,oldEndToken.getNextTokenInChain());
960            oldEndToken.getNextTokenInChain().setPrevTokenInChain(oldStartToken.getPrevTokenInChain());
961        }
962
963        if (this.nodeStatus == ENodeStatus.nsNormal){
964            this.nodeStatus = ENodeStatus.nsRemoved;
965        }
966
967        this.setStartTokenDirectly(null);
968        this.setEndTokenDirectly(null);
969
970    }
971
972
973
974    /**
975     * SQL dialect of this statement.
976     */
977    public EDbVendor dbvendor = EDbVendor.dbvgeneric;
978
979    /**
980     * The parser that generate this node.
981     *
982     * @return parser that generate this node
983     */
984    public TGSqlParser getGsqlparser() {
985        if (gsqlparser == null){
986            if (getStartToken() != null){
987                // TODO  removed in 2.6.8.1, to avoid memory leak in TTypeName
988                // gsqlparser = getStartToken().getGsqlparser();
989            }
990        }
991        return gsqlparser;
992    }
993
994    boolean includingComment = true;
995
996
997    public void setIncludingComment(boolean includingComment) {
998        this.includingComment = includingComment;
999    }
1000
1001    private TGSqlParser gsqlparser = null;
1002
1003    public void setGsqlparser(TGSqlParser gsqlparser) {
1004        this.gsqlparser = gsqlparser;
1005        if (gsqlparser != null){
1006            this.dbvendor = gsqlparser.getDbVendor();
1007        }
1008    }
1009
1010    private int dummyTag = 0;
1011
1012    public void setDummyTag(int dummyTag) {
1013        this.dummyTag = dummyTag;
1014    }
1015
1016    /**
1017     * A temporary value can be used for any purposes
1018     *
1019     * @return a temporary value used for any purposes
1020     */
1021    public int getDummyTag() {
1022        return dummyTag;
1023    }
1024
1025
1026    private TSourceToken startToken;
1027
1028    /**
1029     * The first token in this parse tree node
1030     *
1031     * @return the first token of node
1032     */
1033    public TSourceToken getStartToken() {
1034        return startToken;
1035    }
1036
1037    /**
1038     * The last token of the node
1039     *
1040     * @return the last token of node
1041     */
1042    public TSourceToken getEndToken() {
1043        return endToken;
1044    }
1045
1046    private TSourceToken endToken;
1047    private long lineNo = -1;
1048
1049    /**
1050     * Column position of the first token of this node
1051     *
1052     * @return column position
1053     */
1054    public long getColumnNo() {
1055        if (this.getStartToken() != null){
1056            columnNo = this.getStartToken().columnNo;
1057        }
1058        return columnNo;
1059    }
1060
1061    /**
1062     * Line number of the first token of this node
1063     *
1064     * @return line number
1065     */
1066    public long getLineNo() {
1067        if (this.getStartToken() != null){
1068            lineNo = this.getStartToken().lineNo;
1069        }
1070        return lineNo;
1071    }
1072
1073    private long columnNo = -1;
1074
1075    private int nodeType;
1076    /**
1077         * Set the node type for this node.
1078         *
1079         * @param nodeType The node type.
1080         */
1081        public void setNodeType(int nodeType)
1082        {
1083                this.nodeType = nodeType;
1084        }
1085
1086    /**
1087     * A unique value to distinguish this node from others
1088     *
1089     * @return enum value of node type
1090     * @see gudusoft.gsqlparser.nodes.ENodeType
1091     */
1092        public int getNodeType()
1093        {
1094                return nodeType;
1095        }
1096
1097    String getastext(){ return "";}
1098    String getasprettytext(){ return "";}
1099
1100    /**
1101     * Initialize a query tree node.
1102     * Used internally
1103     *
1104     * @param arg1 first argument
1105     */
1106    public void init(Object arg1)
1107    {
1108    }
1109
1110    public void init(Object arg1, Object arg2)
1111    {
1112    }
1113
1114    public void init(Object arg1, Object arg2, Object arg3)
1115    {
1116    }
1117
1118    public void init(Object arg1, Object arg2, Object arg3, Object arg4)
1119    {
1120    }
1121
1122    public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
1123    {
1124    }
1125
1126    public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6)
1127    {
1128    }
1129
1130    /**
1131     * Analyze the sub-nodes inside this node.
1132     * Such as build the relationship between table and column.
1133     *
1134     * @param psql          SQL statement this node belongs to
1135     * @param plocation    SQL clause this node belongs to
1136     */
1137    public void doParse(TCustomSqlStatement psql, ESqlClause plocation){
1138        
1139    }
1140
1141    public void setStartToken(ArrayList startNode) {
1142        if (startNode == null) return;
1143        if (startNode.size() == 0) return;
1144        TParseTreeNode parseTreeNode = (TParseTreeNode)startNode.get(0);
1145        this.setStartToken(parseTreeNode.getStartToken());
1146    }
1147
1148    public void setEndToken(ArrayList endNode) {
1149        if (endNode == null) return;
1150        if (endNode.size() == 0 ) return;
1151        TParseTreeNode parseTreeNode = (TParseTreeNode)endNode.get(endNode.size()-1);
1152        this.setEndToken(parseTreeNode.getEndToken());
1153    }
1154
1155    public void setStartToken(TSourceToken newStartToken) {
1156        if (newStartToken == null) return;
1157        TSourceToken oldStartToken = this.startToken;
1158        if (oldStartToken != null){
1159            if (oldStartToken.getNodesStartFromThisToken().peek() == this){
1160                oldStartToken.getNodesStartFromThisToken().pop();
1161            }
1162        }
1163        if (newStartToken.getNodesStartFromThisToken().indexOf(this) == -1)
1164        {
1165            newStartToken.getNodesStartFromThisToken().push(this);
1166        }
1167
1168        this.startToken = newStartToken;
1169    }
1170
1171    public void setStartToken(TSourceTokenList startTokenList) {
1172        if (startTokenList == null) return;
1173        this.setStartToken(startTokenList.get(0));
1174    }
1175
1176    public void setStartToken(TParseTreeNode startNode) {
1177        if (startNode == null) return;
1178        this.setStartToken(startNode.getStartToken());
1179    }
1180
1181    public void setStartTokenDirectly(TSourceToken newStartToken) {
1182        this.startToken = newStartToken;
1183    }
1184    public void setEndTokenDirectly(TSourceToken newEndToken) {
1185        this.endToken = newEndToken;
1186    }
1187
1188    public void setEndToken(TSourceToken newEndToken) {
1189        if (newEndToken == null) return;
1190        TSourceToken oldEndToken = this.getEndToken();
1191
1192        if (oldEndToken != null){
1193            if (oldEndToken.getNodesEndWithThisToken().peek() == this){
1194                oldEndToken.getNodesEndWithThisToken().pop();
1195            }
1196        }
1197
1198        if (newEndToken.getNodesEndWithThisToken().indexOf(this) == -1){
1199            newEndToken.getNodesEndWithThisToken().push(this);
1200        }
1201
1202        this.endToken = newEndToken;
1203    }
1204
1205    public void setEndToken(TSourceTokenList endTokenList) {
1206        if (endTokenList == null) return;
1207        this.setEndToken(endTokenList.get(endTokenList.size()-1));
1208    }
1209
1210//    public void setEndToken(TParseTreeNodeList endNodeList) {
1211//        if (endNodeList == null) return;
1212//        this.setEndToken(endNodeList.getEndToken());
1213//    }
1214
1215
1216
1217    public void setEndToken(TFromTableList endNode) {
1218        if (endNode == null) return;
1219        this.setEndToken(endNode.getEndToken());
1220    }
1221
1222    public void setEndToken(TParseTreeNode endNode) {
1223        if (endNode == null) return;
1224        this.setEndToken(endNode.getEndToken());
1225    }
1226
1227    private void setStartTokenToNull(){
1228        this.startToken = null;
1229    }
1230
1231    private void setEndTokenToNull(){
1232        this.endToken = null;
1233    }
1234
1235    private void resetStartAndEndTokenBeforeRemoveTokens(TSourceTokenList stList, int start, int end,boolean isReplace,TSourceToken relpacedStartToken,TSourceToken replacedEndToken){
1236
1237        for(int m = start; m<=end;m++){
1238            if ((m>stList.size()-1) ||(m<0)) break;
1239            TSourceToken deletedToken = stList.get(m);
1240            for(int i=0;i<deletedToken.getNodesStartFromThisToken().size();i++){
1241                TParseTreeNode node = deletedToken.getNodesStartFromThisToken().get(i);
1242                TSourceToken EndSt = node.getEndToken();
1243
1244                if (isReplace&&((m==start)||(m==end))){
1245                    node.setStartToken(relpacedStartToken);
1246                }else{
1247                    if (EndSt == null){
1248                        node.setStartTokenToNull();
1249                    }else{
1250                        if (EndSt.posinlist > end){
1251                            node.setStartToken(stList.get(end+1));
1252                        }else if (EndSt.posinlist < start){
1253                            node.setStartTokenToNull();
1254                            node.setEndTokenToNull();
1255                        }else{
1256                            node.setStartTokenToNull();
1257                            node.setEndTokenToNull();
1258                        }
1259                    }
1260                }
1261            } // nodes start from this token
1262
1263            for (int i=0;i<deletedToken.getNodesEndWithThisToken().size();i++){
1264                TParseTreeNode node = deletedToken.getNodesEndWithThisToken().get(i);
1265                TSourceToken startSt = node.getStartToken();
1266
1267                if (isReplace&&((m==start)||(m==end))){
1268                    node.setEndToken(replacedEndToken);
1269                }else{
1270                    if (startSt == null){
1271                        node.setEndTokenToNull();
1272                    }else{
1273                        if (startSt.posinlist > end){
1274                            node.setStartTokenToNull();
1275                            node.setEndTokenToNull();
1276                        }else if (startSt.posinlist < start){
1277                            node.setEndToken(stList.get(start - 1));
1278                        }else{
1279                            node.setStartTokenToNull();
1280                            node.setEndTokenToNull();
1281                        }
1282                    }
1283                }
1284            }// nodes end with this token
1285
1286        }//loop all tokens to be deleted in this parse tree nodes
1287    }
1288
1289
1290
1291    private void fastResetStartAndEndTokenBeforeRemoveTokens(TSourceTokenList stList, int start, int end,TSourceToken replacedToken){
1292        int[] startEnd;
1293        if (start != end) {
1294            startEnd = new int[2];
1295            startEnd[0] = start;
1296            startEnd[1] = end;
1297        }
1298        else {
1299            startEnd = new int[1];
1300            startEnd[0] = start;
1301        }
1302        for(int m : startEnd){
1303            TSourceToken deletedToken = stList.get(m);
1304            for(int i=0;i<deletedToken.getNodesStartFromThisToken().size();i++){
1305                TParseTreeNode node = deletedToken.getNodesStartFromThisToken().get(i);
1306                node.setStartToken(replacedToken);
1307            } // nodes start from this token
1308
1309            for (int i=0;i<deletedToken.getNodesEndWithThisToken().size();i++){
1310                TParseTreeNode node = deletedToken.getNodesEndWithThisToken().get(i);
1311                node.setEndToken(replacedToken);
1312            }// nodes end with this token
1313
1314        }//loop all tokens to be deleted in this parse tree nodes
1315    }
1316
1317    /**
1318     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1319     * Set the new string text of a node will destroy all the sub-node structure
1320     *
1321     * @param sqlSegment that override original text of this node.
1322     */
1323
1324    /*
1325     * <p>1. new string will be tokenized into a list of source tokens: stlist
1326     * <p>2. link alternativetoken of start token of this node to the first token in stlist generated in step 1.
1327     * <p>3. link back alternative token of last token in stlist to the last token of this node.
1328     * <p>So, if you find a start token of parse tree node has an alternativetoken, then text of this node should be modified by using setString(String sqlSegment)
1329     * <p> if a parse tree node with an alias clause like: tablename as tablealias,
1330     * <p> TTable.setString() will replace whole text "tablename as tablealias" including alias clause.
1331     *
1332     * steps to set new string(sqlSegment) of parse tree node
1333     * length(sqlSegment)=0 or sqlSegment = one space,, means remove this node from parse tree
1334     * length(sqlSegment) > 0 , means replace source tokens of this node with new tokens
1335     *
1336     */
1337    public void setString2(String sqlSegment){
1338
1339//        TGSqlParser l_sqlparser = new TGSqlParser(getGsqlparser().getDbVendor());
1340//        l_sqlparser.sqltext =  sqlSegment;
1341//        l_sqlparser.tokenizeSqltext();
1342
1343        TSingletonParser singletonParser = TSingletonParser.getInstance();
1344
1345        TSourceTokenList stList;
1346        if (this.dbvendor == EDbVendor.dbvgeneric){
1347            stList = singletonParser.getTokens(TGSqlParser.currentDBVendor,sqlSegment);
1348        }else{
1349            stList = singletonParser.getTokens(this.dbvendor,sqlSegment);
1350        }
1351
1352
1353
1354        if ((getStartToken() == null)&&(getEndToken() == null)){
1355            // this is a manually created parse tree node, not created by yacc
1356//            l_sqlparser.sourcetokenlist.get(0).container = l_sqlparser.sourcetokenlist;
1357//            l_sqlparser.sourcetokenlist.get(l_sqlparser.sourcetokenlist.size()-1).container = l_sqlparser.sourcetokenlist;
1358            setStartToken(stList.get(0));
1359            setEndToken(stList.get(stList.size()-1));
1360            return;
1361        }
1362
1363        TSourceToken lcStartToken = getStartToken();
1364        TSourceToken lcEndToken = getEndToken();
1365
1366       // TSourceTokenList stList = lcEndToken.container;
1367
1368        int lcStartTokenPos = lcStartToken.posinlist;
1369        int lcEndTokenPos = lcEndToken.posinlist;
1370
1371
1372        if ((sqlSegment.length()==0) || (sqlSegment == " ")){
1373
1374           //reset start and end token of those parse tree node
1375            resetStartAndEndTokenBeforeRemoveTokens(stList, lcStartTokenPos, lcEndTokenPos,false,null,null);
1376
1377            // remove token from list
1378            for(int i=lcEndTokenPos;i>=lcStartTokenPos;i--){
1379                stList.remove(i);
1380            }
1381
1382            // reindex posinlist after remove token from container
1383            for(int i=lcStartTokenPos;i<stList.size();i++){
1384                stList.get(i).posinlist = i;
1385            }
1386
1387        }else{
1388            //replace
1389           //reset start and end token of those parse tree node
1390            TSourceToken lcReplacedStartToken = stList.get(0);
1391            TSourceToken lcReplacedEndToken = stList.get(stList.size()-1);
1392            resetStartAndEndTokenBeforeRemoveTokens(stList, lcStartTokenPos, lcEndTokenPos,true,lcReplacedStartToken,lcReplacedEndToken);
1393
1394            // remove token from list
1395            for(int i=lcEndTokenPos;i>=lcStartTokenPos;i--){
1396                stList.remove(i);
1397            }
1398
1399
1400//            for(int i = stList.size()-1; i>=0; i--){
1401//                TSourceToken st = stList.get(i);
1402//                st.container = stList;
1403//                stList.add(lcStartTokenPos,st);
1404//            }
1405
1406            // reindex posinlist after remove token from container
1407            for(int i=lcStartTokenPos;i<stList.size();i++){
1408                TSourceToken st = stList.get(i);
1409                st.posinlist =  i;
1410            }
1411
1412        }
1413
1414    }
1415
1416    /**
1417     * Set the text of the node and update the corresponding source tokens synchronously.
1418     * This way, when the toString() method of the node itself or its parent node is called,
1419     * it will return the modified text.
1420     *
1421     * @param sqlSegment
1422     */
1423    public void setString(String sqlSegment){
1424        if (doubleLinkedTokenListToString){
1425            setText(sqlSegment);
1426        }else{
1427            setString2(sqlSegment);
1428        }
1429    }
1430
1431    public void fastSetString(String sqlSegment){
1432
1433        TSourceToken replaceToken = new TSourceToken(sqlSegment);
1434        setText(replaceToken, replaceToken);
1435    }
1436
1437
1438    /**
1439     * return null if no comment is found before this node.
1440     *
1441     * @return the comment before this node
1442     */
1443    public String getCommentBeforeNode(){
1444        String ret = null;
1445        TSourceToken lcStartToken;
1446
1447        if (doubleLinkedTokenListToString){
1448            lcStartToken = getStartToken();
1449            if (lcStartToken ==  null) return null;
1450
1451            TSourceToken lcCurrentToken = lcStartToken.getPrevTokenInChain();
1452            while (lcCurrentToken != null){
1453                if ((lcCurrentToken.tokentype == ETokenType.ttreturn)||(lcCurrentToken.tokentype == ETokenType.ttwhitespace))
1454                {
1455                    if (ret != null){
1456                        ret = lcCurrentToken.toString() + ret ;
1457                    }
1458                    lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1459                    continue;
1460                }
1461                else if (lcCurrentToken.toString().trim().length() == 0) {
1462                    lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1463                    continue;
1464                }
1465                if ((lcCurrentToken.tokencode == TBaseType.cmtslashstar) ||(lcCurrentToken.tokencode == TBaseType.cmtdoublehyphen)
1466                        || lcCurrentToken.toString().startsWith("--") || lcCurrentToken.toString().startsWith("/*")
1467                ){
1468                    if (ret == null){
1469                        ret = lcCurrentToken.toString();
1470                    }else{
1471                        ret = lcCurrentToken.toString()+ ret;
1472                    }
1473
1474                    lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1475                    continue;
1476                }
1477
1478                break;
1479            }
1480        }else{
1481            lcStartToken = getStartToken();
1482            if (lcStartToken ==  null) return null;
1483
1484            TSourceTokenList stList = lcStartToken.container;
1485            if (stList == null) return null;
1486
1487            int b = lcStartToken.posinlist;
1488
1489            for(int i = b - 1 ; i>=0;i--){
1490                TSourceToken st = stList.get(i);
1491                if ((st.tokencode == TBaseType.lexspace)|| (st.tokencode == TBaseType.lexnewline)) continue;
1492                if (st.getAstext().trim().length() == 0) continue;
1493                if ((st.tokencode == TBaseType.cmtslashstar) ||(st.tokencode == TBaseType.cmtdoublehyphen)){
1494                    if (ret == null){
1495                        ret = st.toString();
1496                    }else{
1497                        ret = st.toString()+TBaseType.newline + ret;
1498                    }
1499                    continue;
1500                }
1501                else if (st.getAstext().startsWith("--") || st.getAstext().startsWith("/*")){
1502                    if (ret == null){
1503                        ret = st.toString();
1504                    }else{
1505                        ret = st.toString()+TBaseType.newline + ret;
1506                    }
1507                    continue;
1508                }
1509                break;
1510            }
1511        }
1512
1513        return ret;
1514    }
1515
1516    public String getCommentAfterNode(){
1517        String ret = null;
1518        TSourceToken lcEndToken;
1519
1520        if (!doubleLinkedTokenListToString) return ret;
1521
1522        lcEndToken = getEndToken();
1523        if (lcEndToken ==  null) return null;
1524
1525        TSourceToken lcCurrentToken = lcEndToken;
1526        while (lcCurrentToken != null){
1527            if ((lcCurrentToken.tokentype == ETokenType.ttreturn)||(lcCurrentToken.tokentype == ETokenType.ttwhitespace))
1528            {
1529                if (ret != null){
1530                    ret = lcCurrentToken.toString() + ret ;
1531                }
1532                lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1533                continue;
1534            }
1535            else if (lcCurrentToken.getAstext().trim().length() == 0) {
1536                lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1537                continue;
1538            }
1539            if ((lcCurrentToken.tokencode == TBaseType.cmtslashstar) ||(lcCurrentToken.tokencode == TBaseType.cmtdoublehyphen)
1540                    || lcCurrentToken.getAstext().startsWith("--") || lcCurrentToken.getAstext().startsWith("/*")
1541            ){
1542                if (ret == null){
1543                    ret = lcCurrentToken.toString();
1544                }else{
1545                    ret =  lcCurrentToken.toString()  +ret ;
1546                }
1547
1548                lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1549                continue;
1550            }
1551
1552            break;
1553        }
1554
1555            return ret.trim(); // 移调第一个space or return token if any
1556
1557    }
1558
1559    /**
1560     * String representation of this parse tree node.
1561     * <p>
1562     * This string was generated by collecting text from the start token of this node to the end token of this node.
1563     * If this node was modified, then use {@link #toScript()} to get string representation of this node.
1564     *
1565     * @return string representation of this parse tree node
1566     */
1567    public String toString2(){
1568        String ret = null;
1569        TSourceToken lcStartToken = getStartToken();
1570        if (lcStartToken ==  null) return null;
1571
1572        TSourceToken lcEndToken = getEndToken();
1573        if (lcEndToken ==  null) return null;
1574
1575        TSourceTokenList stList = lcStartToken.container;
1576        if (stList == null) return null;
1577
1578        int b = lcStartToken.posinlist;
1579        int e = lcEndToken.posinlist;
1580
1581        StringBuffer sb = new StringBuffer("");
1582
1583        for(int i= b ; i<=e;i++){
1584              if(!includingComment && ((stList.get(i).tokencode == TBaseType.cmtslashstar) ||(stList.get(i).tokencode == TBaseType.cmtdoublehyphen))){
1585                  continue;
1586              }
1587            sb.append(stList.get(i).toString());
1588        }
1589
1590        return sb.toString();
1591    }
1592
1593    /**
1594     * <p>
1595     * 将一个语法树节点({@code TParseTreeNode})转换回它对应的原始SQL字符串。
1596     * 它通过遍历一个由 {@code startToken}(起始词法单元)和 {@code endToken}(结束词法单元)界定的双向链表来实现这一功能。
1597     * </p>
1598     * <p>
1599     * 这个方法的设计采用了“两遍扫描”(Two-Pass)的策略,也就是使用了两个 {@code while} 循环。
1600     * 第一个循环是预处理阶段,用于清理格式;第二个循环则负责构建最终的字符串。
1601     * </p>
1602     * <h3>第一个 {@code while} 循环</h3>
1603     * <p>
1604     * 这个循环的主要目标是识别并“软删除”多余的换行符(newline tokens)。这种情况尤其在SQL被程序动态修改后容易出现。
1605     * 这个循环本身不构建字符串,只负责分析和更新词法单元的状态。
1606     * 其工作原理是:
1607     * <ol>
1608     *     <li><b>遍历:</b> 它从 {@code startToken} 开始,通过 {@code getNextTokenInChain()} 方法遍历到 {@code endToken}。</li>
1609     *     <li><b>检测链表变化:</b> 它通过比较当前和前一个词法单元的 {@code posinlist}(词法单元在原始完整列表中的位置索引)来判断链表是否“自然”。
1610     *     如果不连续,意味着词法单元被插入或重排了,此时 {@code isChainModified} 标志位会被设为 {@code true}。</li>
1611     *     <li><b>标记换行符:</b> 当遇到第一个换行符时,它会设置一个标志。如果紧接着又遇到了另一个换行符,并且此时链表已经被修改过,
1612     *     那么它会将这个多余的换行符标记为待删除状态。这个逻辑能有效地将代码被修改后可能产生的多个连续换行压缩成一个。</li>
1613     * </ol>
1614     * 简而言之,第一个循环是一个格式清理过程,它为后续生成整洁的字符串做准备。
1615     * </p>
1616     * <h3>第二个 {@code while} 循环</h3>
1617     * <p>
1618     * 这个循环负责从(经过第一步清理后的)词法单元流中实际地构建最终的输出字符串。
1619     * 其工作原理是:
1620     * <ol>
1621     *     <li><b>遍历:</b> 它再次遍历完全相同的词法单元序列。</li>
1622     *     <li><b>构建字符串:</b> 在将每个词法单元追加到 {@code StringBuffer} 之前,它会进行检查。</li>
1623     *     <li><b>跳过特定单元:</b> 如果一个词法单元在第一个循环中被标记为待删除,或者它是一个注释(且配置为不包含注释),那么它将被跳过。</li>
1624     *     <li><b>拼接字符串:</b> 对于所有未被跳过的词法单元,它会将其字符串值追加到 {@code StringBuffer} 中。</li>
1625     * </ol>
1626     * 当这个循环结束后,{@code StringBuffer} 就包含了该语法树节点的最终、重构后的SQL文本。
1627     * </p>
1628     * @return 节点对应的SQL字符串。
1629     */
1630    public String toString(){
1631        if (doubleLinkedTokenListToString){
1632            TSourceToken startToken = getStartToken();
1633            TSourceToken endToken = getEndToken();
1634            
1635            // Return null if boundary tokens are missing
1636            if (startToken == null || endToken == null) {
1637                return null;
1638            }
1639
1640            // First pass: Mark redundant newlines for deletion
1641            // This cleans up formatting issues caused by programmatic SQL modifications
1642            TSourceToken currentToken = startToken;
1643            TSourceToken previousToken = null;
1644            boolean shouldIgnoreNextNewline = false;
1645            boolean tokenChainWasModified = false;
1646
1647            while (currentToken != null) {
1648                // Detect if the token chain has been modified by checking position continuity
1649                if (!tokenChainWasModified && previousToken != null) {
1650                    tokenChainWasModified = (currentToken.posinlist - previousToken.posinlist != 1);
1651                }
1652
1653                // Handle newline tokens - mark redundant ones for deletion
1654                if (currentToken.tokentype == ETokenType.ttreturn) {
1655                    boolean isRedundantNewline = shouldIgnoreNextNewline && tokenChainWasModified;
1656                    
1657                    if (isRedundantNewline) {
1658                        currentToken.tokenstatus = ETokenStatus.tsdeleted;
1659                    } else {
1660                        shouldIgnoreNextNewline = true;
1661                    }
1662                } else if (currentToken.tokentype != ETokenType.ttwhitespace) {
1663                    // Reset newline flag when encountering non-whitespace tokens
1664                    shouldIgnoreNextNewline = false;
1665                }
1666
1667                previousToken = currentToken;
1668                
1669                // Move to next token or break if we've reached the end
1670                if (currentToken.equals(endToken)) {
1671                    break;
1672                }
1673                currentToken = currentToken.getNextTokenInChain();
1674            }
1675
1676            // Second pass: Build the final string by concatenating valid tokens
1677            StringBuffer result = new StringBuffer();
1678            currentToken = startToken;
1679
1680            while (currentToken != null) {
1681                boolean shouldSkipToken = currentToken.tokenstatus == ETokenStatus.tsdeleted ||
1682                    (!includingComment && (currentToken.tokencode == TBaseType.cmtslashstar || 
1683                                         currentToken.tokencode == TBaseType.cmtdoublehyphen)
1684                    );
1685
1686                if (!shouldSkipToken) {
1687                    result.append(currentToken.toString());
1688                }
1689
1690                // Move to next token or break if we've reached the end
1691                if (currentToken.equals(endToken)) {
1692                    break;
1693                }
1694                currentToken = currentToken.getNextTokenInChain();
1695            }
1696
1697            return result.toString();
1698        } else {
1699            return toString2();
1700        }
1701    }
1702
1703
1704    void setText(String nodeText, EDbVendor dbVendor){
1705        this.dbvendor = dbVendor;
1706        setText(nodeText);
1707    }
1708
1709    private boolean tokensInChain = true;
1710
1711    public boolean isTokensInChain() {
1712        return tokensInChain;
1713    }
1714
1715    void setText(TSourceToken startToken, TSourceToken endToken){
1716        TSourceToken newStartToken = startToken;
1717        TSourceToken newEndToken = endToken;
1718
1719        TSourceToken oldStartToken = getStartToken();
1720        TSourceToken oldEndToken = getEndToken();
1721        tokensInChain = false;
1722        if ((oldStartToken == null)||(oldEndToken == null)){
1723            setStartToken(newStartToken);
1724            setEndToken(newEndToken);
1725            nodeStatus = ENodeStatus.nsFlattened;
1726            return;
1727        }
1728
1729        refreshAllNodesTokenCount();
1730
1731        updateStatusOfNodeShareSameTokens(nodeActionUpdateText);
1732
1733        updateNodeWithTheSameStartToken(nodeActionUpdateText,newStartToken);
1734        updateMeNodeWithTheSameEndToken(nodeActionUpdateText,newEndToken);
1735
1736        if (oldStartToken.getPrevTokenInChain() != null){
1737            oldStartToken.getPrevTokenInChain().setNextTokenInChain(newStartToken);
1738            newStartToken.setPrevTokenInChain(oldStartToken.getPrevTokenInChain());
1739        }
1740
1741        if (oldEndToken.getNextTokenInChain() != null){
1742            oldEndToken.getNextTokenInChain().setPrevTokenInChain(newEndToken);
1743            newEndToken.setNextTokenInChain(oldEndToken.getNextTokenInChain());
1744        }
1745
1746        tokensInChain = true;
1747    }
1748    /**
1749     * set text of this node, if the original token of this node
1750     * is in a token chain, then merge the new token into the chain as well.
1751     *
1752     * use double linked list
1753     *
1754     * @param nodeText node text
1755     */
1756    void setText(String nodeText){
1757        if ((this.getNodeStatus() == ENodeStatus.nsRemoved)||(nodeText.length() == 0)) return;
1758       // if ((nodeText.length() == 0)) return;
1759
1760//        TGSqlParser sqlParser = new TGSqlParser(TGSqlParser.currentDBVendor);
1761//        sqlParser.sqltext = nodeText;
1762//        sqlParser.tokenizeSqltext();
1763//        TSourceToken newStartToken = sqlParser.getSourcetokenlist().get(0);
1764//        TSourceToken newEndToken = sqlParser.getSourcetokenlist().get(sqlParser.getSourcetokenlist().size() - 1);
1765//        setText(newStartToken, newEndToken);
1766
1767        TSingletonParser singletonParser = TSingletonParser.getInstance();
1768
1769        TSourceTokenList stList;
1770        if (this.dbvendor == EDbVendor.dbvgeneric){
1771            stList = singletonParser.getTokens(TGSqlParser.currentDBVendor,nodeText);
1772        }else{
1773            stList = singletonParser.getTokens(this.dbvendor,nodeText);
1774        }
1775
1776
1777        TSourceToken newStartToken = stList.get(0);
1778        TSourceToken newEndToken = stList.get(stList.size() - 1);
1779        setText(newStartToken, newEndToken);
1780
1781    }
1782
1783
1784    /**
1785     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1786     * Inserts tokens(from start token to end token ) of this parse tree node at the specified position in this
1787     * list. Shifts the element currently at that position (if any) and
1788     * any subsequent elements to the right .
1789
1790     * @param targetList the new list of source tokens
1791     * @param index  the position insert the targetList
1792     * @return length of the source token list after insert targetList
1793     */
1794     public int addAllMyTokensToTokenList(TSourceTokenList targetList, int index){
1795       int ret = -1;
1796
1797        TSourceToken lcStartToken = getStartToken();
1798        if (lcStartToken ==  null) return ret;
1799
1800        TSourceToken lcEndToken = getEndToken();
1801        if (lcEndToken ==  null) return ret;
1802
1803        TSourceTokenList stList = lcStartToken.container;
1804        if (stList == null) return ret;
1805
1806        for(int i=lcEndToken.posinlist;i>=lcStartToken.posinlist;i--){
1807            targetList.add(index,stList.get(i));
1808            stList.get(i).container = targetList;
1809        }
1810
1811        // reindex posinlist after remove token from container
1812        for(int i=index;i<targetList.size();i++){
1813            targetList.get(i).posinlist = i;
1814        }
1815
1816        return lcEndToken.posinlist - lcStartToken.posinlist + 1;
1817    }
1818
1819    /**
1820     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1821     *
1822     * @param additionalToken usually was comma before or after this parse tree node that also need to be deleted
1823     * @return length of the source token list after remove tokens
1824     */
1825    protected int removeAllMyTokensFromTokenList(TSourceToken additionalToken){
1826
1827        int ret = -1;
1828        TSourceToken lcStartToken = getStartToken();
1829        if (lcStartToken ==  null) return ret;
1830        int lcStartTokenPos = lcStartToken.posinlist;
1831
1832
1833        TSourceToken lcEndToken = getEndToken();
1834        if (lcEndToken ==  null) return ret;
1835        int lcEndTokenPos = lcEndToken.posinlist;
1836
1837
1838        TSourceTokenList stList = lcStartToken.container;
1839        if (stList == null) return ret;
1840
1841        if ((additionalToken != null)&&(additionalToken.tokentype != ETokenType.ttRemoved)){
1842            if (additionalToken.posinlist < lcStartTokenPos){
1843                lcStartTokenPos = additionalToken.posinlist;
1844                additionalToken.tokentype = ETokenType.ttRemoved;
1845            }else if(additionalToken.posinlist > lcEndTokenPos){
1846                lcEndTokenPos = additionalToken.posinlist;
1847                additionalToken.tokentype = ETokenType.ttRemoved;
1848            }
1849        }
1850
1851       //reset start and end token of those parse tree node
1852        resetStartAndEndTokenBeforeRemoveTokens(stList, lcStartTokenPos, lcEndTokenPos,false,null,null);
1853
1854        // remove token from list
1855        for(int i=lcEndTokenPos;i>=lcStartTokenPos;i--){
1856            if ((i>stList.size()-1) ||(i<0)) break;
1857            stList.remove(i);
1858        }
1859
1860        // reindex posinlist after remove token from container
1861        for(int i=lcStartTokenPos;i<stList.size();i++){
1862            if ((i>stList.size()-1) ||(i<0)) break;
1863            stList.get(i).posinlist = i;
1864        }
1865
1866        return lcStartTokenPos - lcEndTokenPos +1;
1867    }
1868
1869    /**
1870     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1871     * parse string to tokens, then add those tokens to the end of this parse tree node
1872     *
1873     * @param sqlSegment
1874     */
1875
1876
1877    /**
1878     * Accept a visitor
1879     *
1880     * @param v visitor is a descendant class of {@link TParseTreeVisitor}
1881     */
1882  public void accept(TParseTreeVisitor v)
1883  {
1884
1885  }
1886
1887    /**
1888     * Accept a visitor to iterate this class and sub-nodes of this class
1889     *
1890     * @param v visitor is a descendant class of {@link TParseTreeVisitor}
1891     */
1892    public void acceptChildren(TParseTreeVisitor v)
1893    {
1894
1895    }
1896
1897  private TScriptGenerator scriptGenerator = null;
1898
1899    /**
1900     * Return the text string of this node, the return value is the same as {@link #toString()} if this node is not modified manually
1901     * after created by parser.
1902     * <br>
1903     * If this node is modified, then use this method to get string representation instead of the {@link #toString()} method.
1904     *
1905     * @return text string of this node
1906     */
1907  public String toScript(){
1908    if (scriptGenerator == null){
1909        scriptGenerator = new TScriptGenerator();
1910    }
1911     return scriptGenerator.generateScript(this);
1912  }
1913
1914  public void setChanged(){
1915      // set the changed status to the first token of this node
1916      if (getStartToken() == null) return;
1917      getStartToken().setTokenstatus(ETokenStatus.tschanged);
1918  }
1919
1920    /**
1921     * Detect wether this node is modified by checking all tokens included in this node.
1922     * @return true if this node is modified.
1923     */
1924  public boolean isChanged(){
1925      boolean ret = false;
1926      TSourceToken lcStartToken = getStartToken();
1927      if (lcStartToken ==  null) return ret;
1928
1929      TSourceToken lcEndToken = getEndToken();
1930      if (lcEndToken ==  null) return ret;
1931
1932      TSourceTokenList stList = lcStartToken.container;
1933      if (stList == null) return ret;
1934
1935      int b = lcStartToken.posinlist;
1936      int e = lcEndToken.posinlist;
1937
1938      for(int i= b ; i<=e;i++){
1939          if (stList.get(i).getTokenstatus() == ETokenStatus.tschanged){
1940              ret = true;
1941              break;
1942          }
1943      }
1944      return ret;
1945  }
1946
1947    protected void doAppendNewNode( TParseTreeNode newNode, TParseTreeNode anchorNode,boolean needCommaBefore){
1948        if (anchorNode != null){
1949            anchorNode.appendNewNode(newNode,needCommaBefore);
1950        }else{
1951            if (!newNode.isTokensInChain()){
1952                this.appendNewNode(newNode,false);
1953                this.setEndToken(newNode.getEndToken());
1954            }
1955        }
1956    }
1957
1958    private TParseTreeNode anchorNode = null;
1959
1960    public void setAnchorNode(TParseTreeNode anchorNode) {
1961        this.anchorNode = anchorNode;
1962    }
1963
1964    public TParseTreeNode getAnchorNode() {
1965        return anchorNode;
1966    }
1967
1968  public void setNewSubNode( TParseTreeNode oldSubNode, TParseTreeNode newSubNode,TParseTreeNode anchorNode){
1969      if (newSubNode == null){
1970          //remove the old node
1971          if (oldSubNode != null){
1972              oldSubNode.removeTokens();
1973          }
1974      }else {
1975          if (oldSubNode == null){
1976              // add a total new where clause, we need to find an anchor
1977              if (newSubNode.getNodeStatus()!=ENodeStatus.nsNormal){
1978                  doAppendNewNode(newSubNode,anchorNode,false);
1979              }
1980
1981          }else{
1982              //replace old where clause
1983              if (newSubNode.getNodeStatus()!=ENodeStatus.nsNormal){
1984                  oldSubNode.replaceWithNewNode(newSubNode);
1985              }
1986          }
1987      }
1988  }
1989
1990    public static boolean subNodeInNode(TParseTreeNode subNode, TParseTreeNode wholeNode){
1991        if (wholeNode == null) return false;
1992        TSourceToken startToken = wholeNode.getStartToken();
1993        if (startToken == null) return false;
1994        TSourceToken endToken = wholeNode.getEndToken();
1995        if (endToken == null) return false;
1996
1997        TSourceToken startTokenOfSubNode = subNode.getStartToken();
1998        if (startTokenOfSubNode == null) return false;
1999        if (startTokenOfSubNode.lineNo < startToken.lineNo) return false;
2000        if ((startTokenOfSubNode.lineNo == startToken.lineNo) && (startTokenOfSubNode.columnNo < startToken.columnNo)) return false;
2001
2002        TSourceToken endTokenOfSubNode = subNode.getEndToken();
2003        if (endTokenOfSubNode == null) return false;
2004
2005        if (endTokenOfSubNode.lineNo > endToken.lineNo) return false;
2006        if ((endTokenOfSubNode.lineNo == endToken.lineNo)&&(endTokenOfSubNode.columnNo > endToken.columnNo)) return false;
2007
2008        return true;
2009    }
2010
2011//  public int setText(EDbVendor dbVendor, String sqlText){
2012//      TGSqlParser sqlParser = new TGSqlParser(dbvendor);
2013//      sqlParser.sqltext = sqlText;
2014//      sqlParser.tokenizeSqltext();
2015//
2016//  }
2017
2018}
2019