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    public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
1131    {
1132    }
1133
1134    public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8)
1135    {
1136    }
1137
1138    public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9)
1139    {
1140    }
1141
1142    public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10)
1143    {
1144    }
1145
1146    /**
1147     * Analyze the sub-nodes inside this node.
1148     * Such as build the relationship between table and column.
1149     *
1150     * @param psql          SQL statement this node belongs to
1151     * @param plocation    SQL clause this node belongs to
1152     */
1153    public void doParse(TCustomSqlStatement psql, ESqlClause plocation){
1154        
1155    }
1156
1157    public void setStartToken(ArrayList startNode) {
1158        if (startNode == null) return;
1159        if (startNode.size() == 0) return;
1160        TParseTreeNode parseTreeNode = (TParseTreeNode)startNode.get(0);
1161        this.setStartToken(parseTreeNode.getStartToken());
1162    }
1163
1164    public void setEndToken(ArrayList endNode) {
1165        if (endNode == null) return;
1166        if (endNode.size() == 0 ) return;
1167        TParseTreeNode parseTreeNode = (TParseTreeNode)endNode.get(endNode.size()-1);
1168        this.setEndToken(parseTreeNode.getEndToken());
1169    }
1170
1171    public void setStartToken(TSourceToken newStartToken) {
1172        if (newStartToken == null) return;
1173        TSourceToken oldStartToken = this.startToken;
1174        if (oldStartToken != null){
1175            if (oldStartToken.getNodesStartFromThisToken().peek() == this){
1176                oldStartToken.getNodesStartFromThisToken().pop();
1177            }
1178        }
1179        if (newStartToken.getNodesStartFromThisToken().indexOf(this) == -1)
1180        {
1181            newStartToken.getNodesStartFromThisToken().push(this);
1182        }
1183
1184        this.startToken = newStartToken;
1185    }
1186
1187    public void setStartToken(TSourceTokenList startTokenList) {
1188        if (startTokenList == null) return;
1189        this.setStartToken(startTokenList.get(0));
1190    }
1191
1192    public void setStartToken(TParseTreeNode startNode) {
1193        if (startNode == null) return;
1194        this.setStartToken(startNode.getStartToken());
1195    }
1196
1197    public void setStartTokenDirectly(TSourceToken newStartToken) {
1198        this.startToken = newStartToken;
1199    }
1200    public void setEndTokenDirectly(TSourceToken newEndToken) {
1201        this.endToken = newEndToken;
1202    }
1203
1204    public void setEndToken(TSourceToken newEndToken) {
1205        if (newEndToken == null) return;
1206        TSourceToken oldEndToken = this.getEndToken();
1207
1208        if (oldEndToken != null){
1209            if (oldEndToken.getNodesEndWithThisToken().peek() == this){
1210                oldEndToken.getNodesEndWithThisToken().pop();
1211            }
1212        }
1213
1214        if (newEndToken.getNodesEndWithThisToken().indexOf(this) == -1){
1215            newEndToken.getNodesEndWithThisToken().push(this);
1216        }
1217
1218        this.endToken = newEndToken;
1219    }
1220
1221    public void setEndToken(TSourceTokenList endTokenList) {
1222        if (endTokenList == null) return;
1223        this.setEndToken(endTokenList.get(endTokenList.size()-1));
1224    }
1225
1226//    public void setEndToken(TParseTreeNodeList endNodeList) {
1227//        if (endNodeList == null) return;
1228//        this.setEndToken(endNodeList.getEndToken());
1229//    }
1230
1231
1232
1233    public void setEndToken(TFromTableList endNode) {
1234        if (endNode == null) return;
1235        this.setEndToken(endNode.getEndToken());
1236    }
1237
1238    public void setEndToken(TParseTreeNode endNode) {
1239        if (endNode == null) return;
1240        this.setEndToken(endNode.getEndToken());
1241    }
1242
1243    private void setStartTokenToNull(){
1244        this.startToken = null;
1245    }
1246
1247    private void setEndTokenToNull(){
1248        this.endToken = null;
1249    }
1250
1251    private void resetStartAndEndTokenBeforeRemoveTokens(TSourceTokenList stList, int start, int end,boolean isReplace,TSourceToken relpacedStartToken,TSourceToken replacedEndToken){
1252
1253        for(int m = start; m<=end;m++){
1254            if ((m>stList.size()-1) ||(m<0)) break;
1255            TSourceToken deletedToken = stList.get(m);
1256            for(int i=0;i<deletedToken.getNodesStartFromThisToken().size();i++){
1257                TParseTreeNode node = deletedToken.getNodesStartFromThisToken().get(i);
1258                TSourceToken EndSt = node.getEndToken();
1259
1260                if (isReplace&&((m==start)||(m==end))){
1261                    node.setStartToken(relpacedStartToken);
1262                }else{
1263                    if (EndSt == null){
1264                        node.setStartTokenToNull();
1265                    }else{
1266                        if (EndSt.posinlist > end){
1267                            node.setStartToken(stList.get(end+1));
1268                        }else if (EndSt.posinlist < start){
1269                            node.setStartTokenToNull();
1270                            node.setEndTokenToNull();
1271                        }else{
1272                            node.setStartTokenToNull();
1273                            node.setEndTokenToNull();
1274                        }
1275                    }
1276                }
1277            } // nodes start from this token
1278
1279            for (int i=0;i<deletedToken.getNodesEndWithThisToken().size();i++){
1280                TParseTreeNode node = deletedToken.getNodesEndWithThisToken().get(i);
1281                TSourceToken startSt = node.getStartToken();
1282
1283                if (isReplace&&((m==start)||(m==end))){
1284                    node.setEndToken(replacedEndToken);
1285                }else{
1286                    if (startSt == null){
1287                        node.setEndTokenToNull();
1288                    }else{
1289                        if (startSt.posinlist > end){
1290                            node.setStartTokenToNull();
1291                            node.setEndTokenToNull();
1292                        }else if (startSt.posinlist < start){
1293                            node.setEndToken(stList.get(start - 1));
1294                        }else{
1295                            node.setStartTokenToNull();
1296                            node.setEndTokenToNull();
1297                        }
1298                    }
1299                }
1300            }// nodes end with this token
1301
1302        }//loop all tokens to be deleted in this parse tree nodes
1303    }
1304
1305
1306
1307    private void fastResetStartAndEndTokenBeforeRemoveTokens(TSourceTokenList stList, int start, int end,TSourceToken replacedToken){
1308        int[] startEnd;
1309        if (start != end) {
1310            startEnd = new int[2];
1311            startEnd[0] = start;
1312            startEnd[1] = end;
1313        }
1314        else {
1315            startEnd = new int[1];
1316            startEnd[0] = start;
1317        }
1318        for(int m : startEnd){
1319            TSourceToken deletedToken = stList.get(m);
1320            for(int i=0;i<deletedToken.getNodesStartFromThisToken().size();i++){
1321                TParseTreeNode node = deletedToken.getNodesStartFromThisToken().get(i);
1322                node.setStartToken(replacedToken);
1323            } // nodes start from this token
1324
1325            for (int i=0;i<deletedToken.getNodesEndWithThisToken().size();i++){
1326                TParseTreeNode node = deletedToken.getNodesEndWithThisToken().get(i);
1327                node.setEndToken(replacedToken);
1328            }// nodes end with this token
1329
1330        }//loop all tokens to be deleted in this parse tree nodes
1331    }
1332
1333    /**
1334     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1335     * Set the new string text of a node will destroy all the sub-node structure
1336     *
1337     * @param sqlSegment that override original text of this node.
1338     */
1339
1340    /*
1341     * <p>1. new string will be tokenized into a list of source tokens: stlist
1342     * <p>2. link alternativetoken of start token of this node to the first token in stlist generated in step 1.
1343     * <p>3. link back alternative token of last token in stlist to the last token of this node.
1344     * <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)
1345     * <p> if a parse tree node with an alias clause like: tablename as tablealias,
1346     * <p> TTable.setString() will replace whole text "tablename as tablealias" including alias clause.
1347     *
1348     * steps to set new string(sqlSegment) of parse tree node
1349     * length(sqlSegment)=0 or sqlSegment = one space,, means remove this node from parse tree
1350     * length(sqlSegment) > 0 , means replace source tokens of this node with new tokens
1351     *
1352     */
1353    public void setString2(String sqlSegment){
1354
1355//        TGSqlParser l_sqlparser = new TGSqlParser(getGsqlparser().getDbVendor());
1356//        l_sqlparser.sqltext =  sqlSegment;
1357//        l_sqlparser.tokenizeSqltext();
1358
1359        TSingletonParser singletonParser = TSingletonParser.getInstance();
1360
1361        TSourceTokenList stList;
1362        if (this.dbvendor == EDbVendor.dbvgeneric){
1363            stList = singletonParser.getTokens(TGSqlParser.currentDBVendor,sqlSegment);
1364        }else{
1365            stList = singletonParser.getTokens(this.dbvendor,sqlSegment);
1366        }
1367
1368
1369
1370        if ((getStartToken() == null)&&(getEndToken() == null)){
1371            // this is a manually created parse tree node, not created by yacc
1372//            l_sqlparser.sourcetokenlist.get(0).container = l_sqlparser.sourcetokenlist;
1373//            l_sqlparser.sourcetokenlist.get(l_sqlparser.sourcetokenlist.size()-1).container = l_sqlparser.sourcetokenlist;
1374            setStartToken(stList.get(0));
1375            setEndToken(stList.get(stList.size()-1));
1376            return;
1377        }
1378
1379        TSourceToken lcStartToken = getStartToken();
1380        TSourceToken lcEndToken = getEndToken();
1381
1382       // TSourceTokenList stList = lcEndToken.container;
1383
1384        int lcStartTokenPos = lcStartToken.posinlist;
1385        int lcEndTokenPos = lcEndToken.posinlist;
1386
1387
1388        if ((sqlSegment.length()==0) || (sqlSegment == " ")){
1389
1390           //reset start and end token of those parse tree node
1391            resetStartAndEndTokenBeforeRemoveTokens(stList, lcStartTokenPos, lcEndTokenPos,false,null,null);
1392
1393            // remove token from list
1394            for(int i=lcEndTokenPos;i>=lcStartTokenPos;i--){
1395                stList.remove(i);
1396            }
1397
1398            // reindex posinlist after remove token from container
1399            for(int i=lcStartTokenPos;i<stList.size();i++){
1400                stList.get(i).posinlist = i;
1401            }
1402
1403        }else{
1404            //replace
1405           //reset start and end token of those parse tree node
1406            TSourceToken lcReplacedStartToken = stList.get(0);
1407            TSourceToken lcReplacedEndToken = stList.get(stList.size()-1);
1408            resetStartAndEndTokenBeforeRemoveTokens(stList, lcStartTokenPos, lcEndTokenPos,true,lcReplacedStartToken,lcReplacedEndToken);
1409
1410            // remove token from list
1411            for(int i=lcEndTokenPos;i>=lcStartTokenPos;i--){
1412                stList.remove(i);
1413            }
1414
1415
1416//            for(int i = stList.size()-1; i>=0; i--){
1417//                TSourceToken st = stList.get(i);
1418//                st.container = stList;
1419//                stList.add(lcStartTokenPos,st);
1420//            }
1421
1422            // reindex posinlist after remove token from container
1423            for(int i=lcStartTokenPos;i<stList.size();i++){
1424                TSourceToken st = stList.get(i);
1425                st.posinlist =  i;
1426            }
1427
1428        }
1429
1430    }
1431
1432    /**
1433     * Set the text of the node and update the corresponding source tokens synchronously.
1434     * This way, when the toString() method of the node itself or its parent node is called,
1435     * it will return the modified text.
1436     *
1437     * @param sqlSegment
1438     */
1439    public void setString(String sqlSegment){
1440        if (doubleLinkedTokenListToString){
1441            setText(sqlSegment);
1442        }else{
1443            setString2(sqlSegment);
1444        }
1445    }
1446
1447    public void fastSetString(String sqlSegment){
1448
1449        TSourceToken replaceToken = new TSourceToken(sqlSegment);
1450        setText(replaceToken, replaceToken);
1451    }
1452
1453
1454    /**
1455     * return null if no comment is found before this node.
1456     *
1457     * @return the comment before this node
1458     */
1459    public String getCommentBeforeNode(){
1460        String ret = null;
1461        TSourceToken lcStartToken;
1462
1463        if (doubleLinkedTokenListToString){
1464            lcStartToken = getStartToken();
1465            if (lcStartToken ==  null) return null;
1466
1467            TSourceToken lcCurrentToken = lcStartToken.getPrevTokenInChain();
1468            while (lcCurrentToken != null){
1469                if ((lcCurrentToken.tokentype == ETokenType.ttreturn)||(lcCurrentToken.tokentype == ETokenType.ttwhitespace))
1470                {
1471                    if (ret != null){
1472                        ret = lcCurrentToken.toString() + ret ;
1473                    }
1474                    lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1475                    continue;
1476                }
1477                else if (lcCurrentToken.toString().trim().length() == 0) {
1478                    lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1479                    continue;
1480                }
1481                if ((lcCurrentToken.tokencode == TBaseType.cmtslashstar) ||(lcCurrentToken.tokencode == TBaseType.cmtdoublehyphen)
1482                        || lcCurrentToken.toString().startsWith("--") || lcCurrentToken.toString().startsWith("/*")
1483                ){
1484                    if (ret == null){
1485                        ret = lcCurrentToken.toString();
1486                    }else{
1487                        ret = lcCurrentToken.toString()+ ret;
1488                    }
1489
1490                    lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1491                    continue;
1492                }
1493
1494                break;
1495            }
1496        }else{
1497            lcStartToken = getStartToken();
1498            if (lcStartToken ==  null) return null;
1499
1500            TSourceTokenList stList = lcStartToken.container;
1501            if (stList == null) return null;
1502
1503            int b = lcStartToken.posinlist;
1504
1505            for(int i = b - 1 ; i>=0;i--){
1506                TSourceToken st = stList.get(i);
1507                if ((st.tokencode == TBaseType.lexspace)|| (st.tokencode == TBaseType.lexnewline)) continue;
1508                if (st.getAstext().trim().length() == 0) continue;
1509                if ((st.tokencode == TBaseType.cmtslashstar) ||(st.tokencode == TBaseType.cmtdoublehyphen)){
1510                    if (ret == null){
1511                        ret = st.toString();
1512                    }else{
1513                        ret = st.toString()+TBaseType.newline + ret;
1514                    }
1515                    continue;
1516                }
1517                else if (st.getAstext().startsWith("--") || st.getAstext().startsWith("/*")){
1518                    if (ret == null){
1519                        ret = st.toString();
1520                    }else{
1521                        ret = st.toString()+TBaseType.newline + ret;
1522                    }
1523                    continue;
1524                }
1525                break;
1526            }
1527        }
1528
1529        return ret;
1530    }
1531
1532    public String getCommentAfterNode(){
1533        String ret = null;
1534        TSourceToken lcEndToken;
1535
1536        if (!doubleLinkedTokenListToString) return ret;
1537
1538        lcEndToken = getEndToken();
1539        if (lcEndToken ==  null) return null;
1540
1541        TSourceToken lcCurrentToken = lcEndToken;
1542        while (lcCurrentToken != null){
1543            if ((lcCurrentToken.tokentype == ETokenType.ttreturn)||(lcCurrentToken.tokentype == ETokenType.ttwhitespace))
1544            {
1545                if (ret != null){
1546                    ret = lcCurrentToken.toString() + ret ;
1547                }
1548                lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1549                continue;
1550            }
1551            else if (lcCurrentToken.getAstext().trim().length() == 0) {
1552                lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1553                continue;
1554            }
1555            if ((lcCurrentToken.tokencode == TBaseType.cmtslashstar) ||(lcCurrentToken.tokencode == TBaseType.cmtdoublehyphen)
1556                    || lcCurrentToken.getAstext().startsWith("--") || lcCurrentToken.getAstext().startsWith("/*")
1557            ){
1558                if (ret == null){
1559                    ret = lcCurrentToken.toString();
1560                }else{
1561                    ret =  lcCurrentToken.toString()  +ret ;
1562                }
1563
1564                lcCurrentToken = lcCurrentToken.getPrevTokenInChain();
1565                continue;
1566            }
1567
1568            break;
1569        }
1570
1571            return ret.trim(); // 移调第一个space or return token if any
1572
1573    }
1574
1575    /**
1576     * String representation of this parse tree node.
1577     * <p>
1578     * This string was generated by collecting text from the start token of this node to the end token of this node.
1579     * If this node was modified, then use {@link #toScript()} to get string representation of this node.
1580     *
1581     * @return string representation of this parse tree node
1582     */
1583    public String toString2(){
1584        String ret = null;
1585        TSourceToken lcStartToken = getStartToken();
1586        if (lcStartToken ==  null) return null;
1587
1588        TSourceToken lcEndToken = getEndToken();
1589        if (lcEndToken ==  null) return null;
1590
1591        TSourceTokenList stList = lcStartToken.container;
1592        if (stList == null) return null;
1593
1594        int b = lcStartToken.posinlist;
1595        int e = lcEndToken.posinlist;
1596
1597        StringBuffer sb = new StringBuffer("");
1598
1599        for(int i= b ; i<=e;i++){
1600              if(!includingComment && ((stList.get(i).tokencode == TBaseType.cmtslashstar) ||(stList.get(i).tokencode == TBaseType.cmtdoublehyphen))){
1601                  continue;
1602              }
1603            sb.append(stList.get(i).toString());
1604        }
1605
1606        return sb.toString();
1607    }
1608
1609    /**
1610     * <p>
1611     * 将一个语法树节点({@code TParseTreeNode})转换回它对应的原始SQL字符串。
1612     * 它通过遍历一个由 {@code startToken}(起始词法单元)和 {@code endToken}(结束词法单元)界定的双向链表来实现这一功能。
1613     * </p>
1614     * <p>
1615     * 这个方法的设计采用了“两遍扫描”(Two-Pass)的策略,也就是使用了两个 {@code while} 循环。
1616     * 第一个循环是预处理阶段,用于清理格式;第二个循环则负责构建最终的字符串。
1617     * </p>
1618     * <h3>第一个 {@code while} 循环</h3>
1619     * <p>
1620     * 这个循环的主要目标是识别并“软删除”多余的换行符(newline tokens)。这种情况尤其在SQL被程序动态修改后容易出现。
1621     * 这个循环本身不构建字符串,只负责分析和更新词法单元的状态。
1622     * 其工作原理是:
1623     * <ol>
1624     *     <li><b>遍历:</b> 它从 {@code startToken} 开始,通过 {@code getNextTokenInChain()} 方法遍历到 {@code endToken}。</li>
1625     *     <li><b>检测链表变化:</b> 它通过比较当前和前一个词法单元的 {@code posinlist}(词法单元在原始完整列表中的位置索引)来判断链表是否“自然”。
1626     *     如果不连续,意味着词法单元被插入或重排了,此时 {@code isChainModified} 标志位会被设为 {@code true}。</li>
1627     *     <li><b>标记换行符:</b> 当遇到第一个换行符时,它会设置一个标志。如果紧接着又遇到了另一个换行符,并且此时链表已经被修改过,
1628     *     那么它会将这个多余的换行符标记为待删除状态。这个逻辑能有效地将代码被修改后可能产生的多个连续换行压缩成一个。</li>
1629     * </ol>
1630     * 简而言之,第一个循环是一个格式清理过程,它为后续生成整洁的字符串做准备。
1631     * </p>
1632     * <h3>第二个 {@code while} 循环</h3>
1633     * <p>
1634     * 这个循环负责从(经过第一步清理后的)词法单元流中实际地构建最终的输出字符串。
1635     * 其工作原理是:
1636     * <ol>
1637     *     <li><b>遍历:</b> 它再次遍历完全相同的词法单元序列。</li>
1638     *     <li><b>构建字符串:</b> 在将每个词法单元追加到 {@code StringBuffer} 之前,它会进行检查。</li>
1639     *     <li><b>跳过特定单元:</b> 如果一个词法单元在第一个循环中被标记为待删除,或者它是一个注释(且配置为不包含注释),那么它将被跳过。</li>
1640     *     <li><b>拼接字符串:</b> 对于所有未被跳过的词法单元,它会将其字符串值追加到 {@code StringBuffer} 中。</li>
1641     * </ol>
1642     * 当这个循环结束后,{@code StringBuffer} 就包含了该语法树节点的最终、重构后的SQL文本。
1643     * </p>
1644     * @return 节点对应的SQL字符串。
1645     */
1646    public String toString(){
1647        if (doubleLinkedTokenListToString){
1648            TSourceToken startToken = getStartToken();
1649            TSourceToken endToken = getEndToken();
1650            
1651            // Return null if boundary tokens are missing
1652            if (startToken == null || endToken == null) {
1653                return null;
1654            }
1655
1656            // First pass: Mark redundant newlines for deletion
1657            // This cleans up formatting issues caused by programmatic SQL modifications
1658            TSourceToken currentToken = startToken;
1659            TSourceToken previousToken = null;
1660            boolean shouldIgnoreNextNewline = false;
1661            boolean tokenChainWasModified = false;
1662
1663            while (currentToken != null) {
1664                // Detect if the token chain has been modified by checking position continuity
1665                if (!tokenChainWasModified && previousToken != null) {
1666                    tokenChainWasModified = (currentToken.posinlist - previousToken.posinlist != 1);
1667                }
1668
1669                // Handle newline tokens - mark redundant ones for deletion
1670                if (currentToken.tokentype == ETokenType.ttreturn) {
1671                    boolean isRedundantNewline = shouldIgnoreNextNewline && tokenChainWasModified;
1672                    
1673                    if (isRedundantNewline) {
1674                        currentToken.tokenstatus = ETokenStatus.tsdeleted;
1675                    } else {
1676                        shouldIgnoreNextNewline = true;
1677                    }
1678                } else if (currentToken.tokentype != ETokenType.ttwhitespace) {
1679                    // Reset newline flag when encountering non-whitespace tokens
1680                    shouldIgnoreNextNewline = false;
1681                }
1682
1683                previousToken = currentToken;
1684                
1685                // Move to next token or break if we've reached the end
1686                if (currentToken.equals(endToken)) {
1687                    break;
1688                }
1689                currentToken = currentToken.getNextTokenInChain();
1690            }
1691
1692            // Second pass: Build the final string by concatenating valid tokens
1693            StringBuffer result = new StringBuffer();
1694            currentToken = startToken;
1695
1696            while (currentToken != null) {
1697                boolean shouldSkipToken = currentToken.tokenstatus == ETokenStatus.tsdeleted ||
1698                    (!includingComment && (currentToken.tokencode == TBaseType.cmtslashstar || 
1699                                         currentToken.tokencode == TBaseType.cmtdoublehyphen)
1700                    );
1701
1702                if (!shouldSkipToken) {
1703                    result.append(currentToken.toString());
1704                }
1705
1706                // Move to next token or break if we've reached the end
1707                if (currentToken.equals(endToken)) {
1708                    break;
1709                }
1710                currentToken = currentToken.getNextTokenInChain();
1711            }
1712
1713            return result.toString();
1714        } else {
1715            return toString2();
1716        }
1717    }
1718
1719
1720    void setText(String nodeText, EDbVendor dbVendor){
1721        this.dbvendor = dbVendor;
1722        setText(nodeText);
1723    }
1724
1725    private boolean tokensInChain = true;
1726
1727    public boolean isTokensInChain() {
1728        return tokensInChain;
1729    }
1730
1731    void setText(TSourceToken startToken, TSourceToken endToken){
1732        TSourceToken newStartToken = startToken;
1733        TSourceToken newEndToken = endToken;
1734
1735        TSourceToken oldStartToken = getStartToken();
1736        TSourceToken oldEndToken = getEndToken();
1737        tokensInChain = false;
1738        if ((oldStartToken == null)||(oldEndToken == null)){
1739            setStartToken(newStartToken);
1740            setEndToken(newEndToken);
1741            nodeStatus = ENodeStatus.nsFlattened;
1742            return;
1743        }
1744
1745        refreshAllNodesTokenCount();
1746
1747        updateStatusOfNodeShareSameTokens(nodeActionUpdateText);
1748
1749        updateNodeWithTheSameStartToken(nodeActionUpdateText,newStartToken);
1750        updateMeNodeWithTheSameEndToken(nodeActionUpdateText,newEndToken);
1751
1752        if (oldStartToken.getPrevTokenInChain() != null){
1753            oldStartToken.getPrevTokenInChain().setNextTokenInChain(newStartToken);
1754            newStartToken.setPrevTokenInChain(oldStartToken.getPrevTokenInChain());
1755        }
1756
1757        if (oldEndToken.getNextTokenInChain() != null){
1758            oldEndToken.getNextTokenInChain().setPrevTokenInChain(newEndToken);
1759            newEndToken.setNextTokenInChain(oldEndToken.getNextTokenInChain());
1760        }
1761
1762        tokensInChain = true;
1763    }
1764    /**
1765     * set text of this node, if the original token of this node
1766     * is in a token chain, then merge the new token into the chain as well.
1767     *
1768     * use double linked list
1769     *
1770     * @param nodeText node text
1771     */
1772    void setText(String nodeText){
1773        if ((this.getNodeStatus() == ENodeStatus.nsRemoved)||(nodeText.length() == 0)) return;
1774       // if ((nodeText.length() == 0)) return;
1775
1776//        TGSqlParser sqlParser = new TGSqlParser(TGSqlParser.currentDBVendor);
1777//        sqlParser.sqltext = nodeText;
1778//        sqlParser.tokenizeSqltext();
1779//        TSourceToken newStartToken = sqlParser.getSourcetokenlist().get(0);
1780//        TSourceToken newEndToken = sqlParser.getSourcetokenlist().get(sqlParser.getSourcetokenlist().size() - 1);
1781//        setText(newStartToken, newEndToken);
1782
1783        TSingletonParser singletonParser = TSingletonParser.getInstance();
1784
1785        TSourceTokenList stList;
1786        if (this.dbvendor == EDbVendor.dbvgeneric){
1787            stList = singletonParser.getTokens(TGSqlParser.currentDBVendor,nodeText);
1788        }else{
1789            stList = singletonParser.getTokens(this.dbvendor,nodeText);
1790        }
1791
1792
1793        TSourceToken newStartToken = stList.get(0);
1794        TSourceToken newEndToken = stList.get(stList.size() - 1);
1795        setText(newStartToken, newEndToken);
1796
1797    }
1798
1799
1800    /**
1801     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1802     * Inserts tokens(from start token to end token ) of this parse tree node at the specified position in this
1803     * list. Shifts the element currently at that position (if any) and
1804     * any subsequent elements to the right .
1805
1806     * @param targetList the new list of source tokens
1807     * @param index  the position insert the targetList
1808     * @return length of the source token list after insert targetList
1809     */
1810     public int addAllMyTokensToTokenList(TSourceTokenList targetList, int index){
1811       int ret = -1;
1812
1813        TSourceToken lcStartToken = getStartToken();
1814        if (lcStartToken ==  null) return ret;
1815
1816        TSourceToken lcEndToken = getEndToken();
1817        if (lcEndToken ==  null) return ret;
1818
1819        TSourceTokenList stList = lcStartToken.container;
1820        if (stList == null) return ret;
1821
1822        for(int i=lcEndToken.posinlist;i>=lcStartToken.posinlist;i--){
1823            targetList.add(index,stList.get(i));
1824            stList.get(i).container = targetList;
1825        }
1826
1827        // reindex posinlist after remove token from container
1828        for(int i=index;i<targetList.size();i++){
1829            targetList.get(i).posinlist = i;
1830        }
1831
1832        return lcEndToken.posinlist - lcStartToken.posinlist + 1;
1833    }
1834
1835    /**
1836     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1837     *
1838     * @param additionalToken usually was comma before or after this parse tree node that also need to be deleted
1839     * @return length of the source token list after remove tokens
1840     */
1841    protected int removeAllMyTokensFromTokenList(TSourceToken additionalToken){
1842
1843        int ret = -1;
1844        TSourceToken lcStartToken = getStartToken();
1845        if (lcStartToken ==  null) return ret;
1846        int lcStartTokenPos = lcStartToken.posinlist;
1847
1848
1849        TSourceToken lcEndToken = getEndToken();
1850        if (lcEndToken ==  null) return ret;
1851        int lcEndTokenPos = lcEndToken.posinlist;
1852
1853
1854        TSourceTokenList stList = lcStartToken.container;
1855        if (stList == null) return ret;
1856
1857        if ((additionalToken != null)&&(additionalToken.tokentype != ETokenType.ttRemoved)){
1858            if (additionalToken.posinlist < lcStartTokenPos){
1859                lcStartTokenPos = additionalToken.posinlist;
1860                additionalToken.tokentype = ETokenType.ttRemoved;
1861            }else if(additionalToken.posinlist > lcEndTokenPos){
1862                lcEndTokenPos = additionalToken.posinlist;
1863                additionalToken.tokentype = ETokenType.ttRemoved;
1864            }
1865        }
1866
1867       //reset start and end token of those parse tree node
1868        resetStartAndEndTokenBeforeRemoveTokens(stList, lcStartTokenPos, lcEndTokenPos,false,null,null);
1869
1870        // remove token from list
1871        for(int i=lcEndTokenPos;i>=lcStartTokenPos;i--){
1872            if ((i>stList.size()-1) ||(i<0)) break;
1873            stList.remove(i);
1874        }
1875
1876        // reindex posinlist after remove token from container
1877        for(int i=lcStartTokenPos;i<stList.size();i++){
1878            if ((i>stList.size()-1) ||(i<0)) break;
1879            stList.get(i).posinlist = i;
1880        }
1881
1882        return lcStartTokenPos - lcEndTokenPos +1;
1883    }
1884
1885    /**
1886     * @deprecated since v1.8.8.0, use scriptWriter technology to modify the node
1887     * parse string to tokens, then add those tokens to the end of this parse tree node
1888     *
1889     * @param sqlSegment
1890     */
1891
1892
1893    /**
1894     * Accept a visitor
1895     *
1896     * @param v visitor is a descendant class of {@link TParseTreeVisitor}
1897     */
1898  public void accept(TParseTreeVisitor v)
1899  {
1900
1901  }
1902
1903    /**
1904     * Accept a visitor to iterate this class and sub-nodes of this class
1905     *
1906     * @param v visitor is a descendant class of {@link TParseTreeVisitor}
1907     */
1908    public void acceptChildren(TParseTreeVisitor v)
1909    {
1910
1911    }
1912
1913  private TScriptGenerator scriptGenerator = null;
1914
1915    /**
1916     * Return the text string of this node, the return value is the same as {@link #toString()} if this node is not modified manually
1917     * after created by parser.
1918     * <br>
1919     * If this node is modified, then use this method to get string representation instead of the {@link #toString()} method.
1920     *
1921     * @return text string of this node
1922     */
1923  public String toScript(){
1924    if (scriptGenerator == null){
1925        scriptGenerator = new TScriptGenerator();
1926    }
1927     return scriptGenerator.generateScript(this);
1928  }
1929
1930  public void setChanged(){
1931      // set the changed status to the first token of this node
1932      if (getStartToken() == null) return;
1933      getStartToken().setTokenstatus(ETokenStatus.tschanged);
1934  }
1935
1936    /**
1937     * Detect wether this node is modified by checking all tokens included in this node.
1938     * @return true if this node is modified.
1939     */
1940  public boolean isChanged(){
1941      boolean ret = false;
1942      TSourceToken lcStartToken = getStartToken();
1943      if (lcStartToken ==  null) return ret;
1944
1945      TSourceToken lcEndToken = getEndToken();
1946      if (lcEndToken ==  null) return ret;
1947
1948      TSourceTokenList stList = lcStartToken.container;
1949      if (stList == null) return ret;
1950
1951      int b = lcStartToken.posinlist;
1952      int e = lcEndToken.posinlist;
1953
1954      for(int i= b ; i<=e;i++){
1955          if (stList.get(i).getTokenstatus() == ETokenStatus.tschanged){
1956              ret = true;
1957              break;
1958          }
1959      }
1960      return ret;
1961  }
1962
1963    protected void doAppendNewNode( TParseTreeNode newNode, TParseTreeNode anchorNode,boolean needCommaBefore){
1964        if (anchorNode != null){
1965            anchorNode.appendNewNode(newNode,needCommaBefore);
1966        }else{
1967            if (!newNode.isTokensInChain()){
1968                this.appendNewNode(newNode,false);
1969                this.setEndToken(newNode.getEndToken());
1970            }
1971        }
1972    }
1973
1974    private TParseTreeNode anchorNode = null;
1975
1976    public void setAnchorNode(TParseTreeNode anchorNode) {
1977        this.anchorNode = anchorNode;
1978    }
1979
1980    public TParseTreeNode getAnchorNode() {
1981        return anchorNode;
1982    }
1983
1984  public void setNewSubNode( TParseTreeNode oldSubNode, TParseTreeNode newSubNode,TParseTreeNode anchorNode){
1985      if (newSubNode == null){
1986          //remove the old node
1987          if (oldSubNode != null){
1988              oldSubNode.removeTokens();
1989          }
1990      }else {
1991          if (oldSubNode == null){
1992              // add a total new where clause, we need to find an anchor
1993              if (newSubNode.getNodeStatus()!=ENodeStatus.nsNormal){
1994                  doAppendNewNode(newSubNode,anchorNode,false);
1995              }
1996
1997          }else{
1998              //replace old where clause
1999              if (newSubNode.getNodeStatus()!=ENodeStatus.nsNormal){
2000                  oldSubNode.replaceWithNewNode(newSubNode);
2001              }
2002          }
2003      }
2004  }
2005
2006    public static boolean subNodeInNode(TParseTreeNode subNode, TParseTreeNode wholeNode){
2007        if (wholeNode == null) return false;
2008        TSourceToken startToken = wholeNode.getStartToken();
2009        if (startToken == null) return false;
2010        TSourceToken endToken = wholeNode.getEndToken();
2011        if (endToken == null) return false;
2012
2013        TSourceToken startTokenOfSubNode = subNode.getStartToken();
2014        if (startTokenOfSubNode == null) return false;
2015        if (startTokenOfSubNode.lineNo < startToken.lineNo) return false;
2016        if ((startTokenOfSubNode.lineNo == startToken.lineNo) && (startTokenOfSubNode.columnNo < startToken.columnNo)) return false;
2017
2018        TSourceToken endTokenOfSubNode = subNode.getEndToken();
2019        if (endTokenOfSubNode == null) return false;
2020
2021        if (endTokenOfSubNode.lineNo > endToken.lineNo) return false;
2022        if ((endTokenOfSubNode.lineNo == endToken.lineNo)&&(endTokenOfSubNode.columnNo > endToken.columnNo)) return false;
2023
2024        return true;
2025    }
2026
2027//  public int setText(EDbVendor dbVendor, String sqlText){
2028//      TGSqlParser sqlParser = new TGSqlParser(dbvendor);
2029//      sqlParser.sqltext = sqlText;
2030//      sqlParser.tokenizeSqltext();
2031//
2032//  }
2033
2034}
2035