001package gudusoft.gsqlparser;
002
003import gudusoft.gsqlparser.nodes.TObjectName;
004import gudusoft.gsqlparser.nodes.TParseTreeNode;
005
006import java.util.Stack;
007
008/**
009 * Represents a source token which is the basic syntactic unit of SQL.
010 * A token can be a key word, an identifier, a quoted identifier, a literal (or constant), or a special character symbol.
011 * Tokens are normally separated by whitespace (space, tab, newline), but need not be if there is no ambiguity
012 * (which is generally only the case if a special character is adjacent to some other token type).
013 * <p>
014 * The parse tree node consists of source tokens.
015 * <p>
016 * <br>A list of source token will be available after parse or tokenize the input SQL.
017 * <pre>
018 * {@code
019 *
020 *   TGSqlParser sqlparser = new TGSqlParser(EDbVendor.dbvoracle);
021 *   sqlparser.sqltext = "select col from t";
022 *   int ret = sqlparser.parse();
023 *   if (ret == 0){
024 *    for(int i=0;i<sqlparser.sourcetokenlist.size();i++){
025 *      TSourceToken st =  sqlparser.sourcetokenlist.get(i);
026 *      System.out.println(st.tokentype.toString()+" "+st.toString());
027 *    }
028 *   }else{
029 *     System.out.println(sqlparser.getErrormessage());
030 *   }
031 *
032 * }
033 * </pre>
034 * Get a list of source tokens after call the method {@link gudusoft.gsqlparser.TGSqlParser#parse() }
035 * or just call {@link gudusoft.gsqlparser.TGSqlParser#tokenizeSqltext()} if you only
036 * need to access tokens of input SQL without generating the full parse tree.
037 * <br><br>
038 * {@link #tokencode} is the unique id represents the type of token,
039 * some typical tokens are: whitespace, return, keyword, identifier. This value is mainly used by the parser internally.
040 * <p>
041 * {@link #tokentype} uniquely identify the token type in a more meaningful way. It's more easier to use
042 * this field in your program than tokencode.
043 */
044
045public class TSourceToken implements Cloneable {
046
047    public TSourceToken clone(){
048        TSourceToken cloneObject = new TSourceToken();
049        cloneObject.tokencode = this.tokencode;
050        cloneObject.tokenstatus = this.tokenstatus;
051        cloneObject.tokentype = this.tokentype;
052        cloneObject.dbObjectType = this.dbObjectType;
053        cloneObject.setAstext(this.getAstext());
054        cloneObject.lineNo = this.lineNo;
055        cloneObject.columnNo = this.columnNo;
056
057        return cloneObject;
058    }
059
060    public ESqlClause location = ESqlClause.unknown;
061
062    public static void  concatInChain(TSourceToken st1, TSourceToken st2){
063        st1.setNextTokenInChain(st2);
064        st2.setPrevTokenInChain(st1);
065    }
066
067    public void insertANewTokenAfterMe(TSourceToken newToken){
068        newToken.setNextTokenInChain(this.getNextTokenInChain());
069        if (this.getNextTokenInChain() != null){
070            this.getNextTokenInChain().setPrevTokenInChain(newToken);
071        }
072
073        newToken.setPrevTokenInChain(this);
074        this.setNextTokenInChain(newToken);
075    }
076
077    public void insertANewTokenBeforeMe(TSourceToken newToken){
078        newToken.setPrevTokenInChain(this.getPrevTokenInChain());
079        if (this.getPrevTokenInChain() != null){
080            this.getPrevTokenInChain().setNextTokenInChain(newToken);
081        }
082
083        newToken.setNextTokenInChain(this);
084        this.setPrevTokenInChain(newToken);
085    }
086
087    public  void updateNodeEndWithThisToken(){
088        for(int i=0;i<this.getNodesEndWithThisToken().size();i++){
089            TParseTreeNode node = this.getNodesEndWithThisToken().get(i);
090                if (this.getPrevTokenInChain() != null){
091                    node.setEndTokenDirectly(this.getPrevTokenInChain());
092                    this.getPrevTokenInChain().getNodesEndWithThisToken().push(node);
093                }
094        }
095    }
096
097    public void updateNodeStartWithThisToken(){
098        for(int i=0;i<this.getNodesStartFromThisToken().size();i++){
099            TParseTreeNode node = this.getNodesStartFromThisToken().get(i);
100            if (this.getNextTokenInChain() != null){
101                node.setStartTokenDirectly(this.getNextTokenInChain());
102                this.getNextTokenInChain().getNodesStartFromThisToken().push(node);
103            }
104        }
105    }
106
107    public void removeFromChain(){
108        if (this.getPrevTokenInChain() != null){
109            this.getPrevTokenInChain().setNextTokenInChain(this.getNextTokenInChain());
110            if (this.getNextTokenInChain() != null){
111                this.getNextTokenInChain().setPrevTokenInChain(this.getPrevTokenInChain());
112            }
113        }
114    }
115    private TSourceToken prevTokenInChain = null, nextTokenInChain = null;
116
117    public void setPrevTokenInChain(TSourceToken prevTokenInChain) {
118        this.prevTokenInChain = prevTokenInChain;
119    }
120
121    public void setNextTokenInChain(TSourceToken nextTokenInChain) {
122        this.nextTokenInChain = nextTokenInChain;
123    }
124
125    public TSourceToken getPrevTokenInChain() {
126        return prevTokenInChain;
127    }
128
129    public TSourceToken getNextTokenInChain() {
130        return nextTokenInChain;
131    }
132
133    public ETokenStatus getTokenstatus() {
134        return tokenstatus;
135    }
136
137    public int getQuoteSymbolLength(){
138        String pstr = this.toString();
139        if (pstr.startsWith("'")){
140            return 1;
141        }else if (pstr.startsWith("$")){
142            return this.dolqstart.length();
143        }else {
144            return 0;
145        }
146    }
147    public String getQuotedString(){
148        String pstr = this.toString();
149        if (pstr.startsWith("'")){
150            return pstr.substring(1,pstr.length()-1);
151        }else if (pstr.startsWith("$")){
152            return pstr.substring(this.dolqstart.length(),pstr.length()-  this.dolqstart.length());
153        }else {
154            return "";
155        }
156    }
157
158    /**
159     * String literal in SQL usually inside ``.
160     * Quoted identifier in SQL usually surrounded by [ and ],
161     * This method only returns the text inside those surroundings.
162     *
163     * @return text inside `` and []
164     */
165    public String getTextWithoutQuoted(){
166        if ((toString().startsWith("'"))||(toString().startsWith("["))||(toString().startsWith("\""))){
167            return  toString().substring(1, toString().length() - 1);
168        }else
169          return  toString();
170    }
171
172    /**
173     * Class constructor
174     */
175    public  TSourceToken(){
176    }
177
178    /**
179     * Class constructor, set a string value.
180     *
181     * @param s the string value this toke represent for.
182     */
183    public TSourceToken(String s){
184        setAstext(s);
185    }
186
187    /**
188     * The string text of this token
189     *
190     * @return the string text of this token
191     */
192    public String toScript(){
193        return getAstext();
194    }
195
196
197    public int prevTokenCode = 0;
198    /**
199     * Unique id of this token used by parser internally.
200     * check available value start from {@link gudusoft.gsqlparser.TBaseType#cmtslashstar}
201     */
202    public int tokencode;
203
204    /**
205     * the line number of the first character in this token
206     */
207    public long lineNo;
208
209    /**
210     * the column number of the first character in this token
211     */
212    public long columnNo;
213
214    /**
215     * Token's offset from the beginning of the input query.
216     * <pre>
217     *     {@code
218     *    public void testOffset(){
219     *        TGSqlParser sqlparser = new TGSqlParser(EDbVendor.dbvoracle);
220     *        sqlparser.sqltext = "select f from t\n" +
221     *                "where f>1\n";
222     *        assertTrue(sqlparser.parse() == 0);
223     *        for (int i=0;i<sqlparser.sourcetokenlist.size();i++){
224     *            TSourceToken st = sqlparser.sourcetokenlist.get(i);
225     *            String textFromOffset = sqlparser.sqltext.toString().substring((int)st.offset,(int)st.offset+st.toString().length());
226     *            assertTrue(st.toString().equalsIgnoreCase(textFromOffset));
227     *        }
228     *    }
229     *     }
230     * </pre>
231     */
232    public long offset;
233
234    /**
235     * Uniquely identify the token type in a more meaningful way. It's more easier to use
236     * this field in your program than tokencode.
237     * check available value in {@link gudusoft.gsqlparser.ETokenType ETokenType}
238     */
239    public ETokenType tokentype;
240
241    /**
242     * Container for this token which is a list of source token, this is the reference to {@link gudusoft.gsqlparser.TGSqlParser#sourcetokenlist}
243     */
244    public TSourceTokenList container;
245
246    /**
247     * When a vendor tokenizer replaces one source token by several (BigQuery
248     * splits {@code `project.dataset.table`} into {@code `project` . `dataset` . `table`}
249     * so the grammar sees the parts), every generated token points here at the
250     * token it was split from; {@code null} for ordinary tokens. Consumers that
251     * must reproduce the user's spelling (the SQL formatter) print the original
252     * once for the whole run instead of the parts.
253     *
254     * @since 4.2.9
255     */
256    public TSourceToken splitFrom;
257    
258    /**
259     * Index of this token in the {@link #container}, start from 0
260     *
261     * <pre>
262     *     {@code
263     *    public void testPosinList(){
264     *        TGSqlParser sqlparser = new TGSqlParser(EDbVendor.dbvoracle);
265     *        sqlparser.sqltext = "select f from t\n" +
266     *                "where f>1\n";
267     *        assertTrue(sqlparser.parse() == 0);
268     *        for (int i=0;i<sqlparser.sourcetokenlist.size();i++){
269     *            assertTrue(i == sqlparser.sourcetokenlist.get(i).posinlist);
270     *        }
271     *    }
272     *     }
273     * </pre>
274     */
275    public int posinlist;
276
277    /**
278     * The text content of this token.
279     *
280     * <p>This public field is maintained for backwards compatibility with existing code.
281     * New code should use getter/setter methods instead of direct field access.</p>
282     *
283     * <h3>Recommended Usage:</h3>
284     * <ul>
285     *   <li><b>Reading text:</b> Use {@link #toString()} method</li>
286     *   <li><b>Writing text:</b> Use {@link #setAstext(String)} method</li>
287     * </ul>
288     *
289     * @see #toString() - Safe way to read token text
290     * @see #getAstext() - Alternative getter method
291     * @see #setAstext(String) - Proper way to set token text
292     * @since 1.0 (public field maintained for backwards compatibility)
293     */
294    public String astext;
295
296    public void appendText(TSourceToken st){
297
298        if (this.toString().startsWith(".")){
299            // dataset_id.3_ST_LEAK_ORDERS, .3_ST_LEAK_ORDERS 是由 .3 和_ST_LEAK_ORDERS组成
300            // 在拼接 .3 和_ST_LEAK_ORDERS 时,需要把 .3 中的 . 去掉
301            this.setAstext(this.toString().substring(1,this.toString().length()));
302            this.insertANewTokenBeforeMe(new TSourceToken("."));
303        }
304        if (st.toString().endsWith(".")){
305            this.setAstext(this.getAstext() +st.toString().substring(0,st.toString().length()-1));
306            //st.tokenstatus = ETokenStatus.tsdeleted; // make sure this token don't appear in toString()
307
308            // 以下代码确保 920778. 后面的 . 在 TObjectName.toString() 中出现
309            //  prod-gcp-data-lakehouse-920778.EXTERNALDATA.SMS_LOCATION
310
311            st.tokencode = '.';
312            st.setAstext(".");
313            st.tokentype = ETokenType.ttperiod;
314        }else{
315            this.setAstext(this.getAstext() +st.toString());
316            st.tokenstatus = ETokenStatus.tsdeleted;
317        }
318    }
319
320    public void appendText(String text){
321        this.setAstext(this.getAstext() +text);
322    }
323
324    public void insertText(TSourceToken st){
325        this.setAstext(st.toString()+ this.getAstext());
326        st.tokenstatus = ETokenStatus.tsdeleted;
327    }
328    public void setTextWithBackup(String newText){
329
330        prevsourcecode = getAstext();
331        tag = TBaseType.tag_token_value_changed_in_on_canonical;
332
333        setAstext(newText);
334    }
335
336    public boolean isChangedInAsCanonical(){
337        return tag == TBaseType.tag_token_value_changed_in_on_canonical;
338    }
339
340    public void setTokenstatus(ETokenStatus tokenstatus) {
341        this.tokenstatus = tokenstatus;
342    }
343
344    // private ETokenStatus statusForRewrite = ETokenStatus.tsoriginal;
345
346    /**
347     * Maintenance the status of this token during lex and parsing.
348     * Used by the parser internally.
349     */
350    public ETokenStatus tokenstatus;
351
352    /**
353     * Start part of Dollar-quoted String Constants of PostgreSQL.
354     *
355     * While the standard syntax for specifying string constants is usually convenient,
356     * it can be difficult to understand when the desired string contains many single quotes or backslashes,
357     * since each of those must be doubled. To allow more readable queries in such situations,
358     * PostgreSQL provides another way, called "dollar quoting", to write string constants.
359     * A dollar-quoted string constant consists of a dollar sign ($), an optional "tag" of zero or more characters,
360     * another dollar sign, an arbitrary sequence of characters that makes up the string content, a dollar sign,
361     * the same tag that began this dollar quote, and a dollar sign.
362     * <p></p>
363     * For example, here are two different ways to specify the string "Dianne's horse" using dollar quoting:
364     * <pre>
365     *     $$Dianne's horse$$
366     *     $SomeTag$Dianne's horse$SomeTag$
367     * </pre>
368     *
369     * This field will return $$ and $SomeTag$ accordingly.
370     */
371    public String  dolqstart;//postgresql, start part of Dollar-quoted String Constants
372
373    private String prevsourcecode;
374
375    public void restoreText(){
376        setAstext(prevsourcecode);
377        tag = 0;
378    }
379
380    public boolean insqlpluscmd;
381
382    /*
383     * source token can be end token of one or more parse tree nodes
384     * NodesEndWithThisToken includes all those parse tree nodes
385     */
386    private Stack<TParseTreeNode> NodesEndWithThisToken = null;
387
388    /**
389     *
390     *
391     * A list of nodes whose end token is this token.
392     *
393     * @return A list of nodes whose end token is this token.
394     */
395    public Stack<TParseTreeNode> getNodesEndWithThisToken() {
396        if (this.NodesEndWithThisToken == null){
397            this.NodesEndWithThisToken = new Stack<TParseTreeNode>();
398        }
399        return NodesEndWithThisToken;
400    }
401
402    /*
403     * source token can be start token of one or more parse tree nodes,
404     * NodesStartFromThisToken includes those parse tree nodes.
405     */
406    private Stack<TParseTreeNode> NodesStartFromThisToken = null;
407
408    /**
409     *
410     * A list of node whose start token is this token
411     *
412     * @return A list of node whose start token is this token
413     */
414    public Stack<TParseTreeNode> getNodesStartFromThisToken() {
415        if (this.NodesStartFromThisToken == null){
416            this.NodesStartFromThisToken = new Stack<TParseTreeNode>();
417        }
418        return NodesStartFromThisToken;
419    }
420
421
422    /**
423     * SQL statement that owns this token.
424     */
425    public TCustomSqlStatement stmt;
426
427    /**
428     * Space to save a value for temporary use
429     */
430    public int tag;
431
432    /**
433     * @deprecated use {@link #setDbObjectType} instead.
434     *
435     * @param dbObjType the database object type
436     */
437    public void setDbObjType(int dbObjType) {
438        this.dbObjType = dbObjType;
439    }
440
441    /**
442     * Token in a {@link TObjectName} has the same database object type as the objectName.
443     * Please use {@link gudusoft.gsqlparser.nodes.TObjectName#getDbObjectType} instead of this method if possible.
444     *
445     * @return the type of the database object
446     */
447    public int getDbObjType() {
448
449        return dbObjType;
450    }
451
452    /**
453     * Set the database object type of this token
454     *
455     * @param dbObjectType database object type
456     */
457    public void setDbObjectType(EDbObjectType dbObjectType) {
458        this.dbObjectType = dbObjectType;
459    }
460
461    /**
462     * Token in a {@link TObjectName} has the same database object type as the objectName.
463     * Please use {@link gudusoft.gsqlparser.nodes.TObjectName#getDbObjectType} instead of this method if possible.
464     *
465     * @return the type of the database object
466     */
467    public EDbObjectType getDbObjectType() {
468
469        return dbObjectType;
470    }
471
472    private EDbObjectType dbObjectType = EDbObjectType.unknown;
473    private int dbObjType = TObjectName.ttobjUnknown;
474
475
476
477    /**
478     * The database vendor which the SQL script includes this token will run against
479     *
480     * @param dbvendor the database vendor such as Oracle, DB2 and so on.
481     */
482    public void setDbvendor(EDbVendor dbvendor) {
483        this.dbvendor = dbvendor;
484    }
485
486    /**
487     * The database vendor which the SQL script includes this token will run against
488     * @return dbvendor the database vendor such as Oracle, DB2 and so on.
489     */
490    public EDbVendor getDbvendor() {
491
492        return dbvendor;
493    }
494
495    private EDbVendor dbvendor;
496
497    /**
498     * set new string of this token
499     *
500     * @param str the new string text
501     */
502    public void setString(String str){
503        setAstext(str);
504    }
505
506    /**
507     * The original string text for this token.
508     * @return  the string text
509     */
510    public String toString(){
511        return astext != null ? astext : "";
512    }
513    
514    
515
516    /**
517     * String text with the debug information such as coordinate, token code, token type
518     *
519     * @return the string value with full debug info
520     */
521    public String toStringDebug(){
522      String ret = lineNo +","+ columnNo +","+ getAstext().length()+","+offset+","+tokencode+" "+tokentype;
523      if (tokencode == TBaseType.cmtslashstar)
524        {ret = ret +" multi line comment";}
525      else if (tokencode == TBaseType.cmtdoublehyphen)
526        {ret = ret +" single line comment";}
527      else if (tokencode == TBaseType.lexspace)
528        {ret = ret +" space"; }
529      else if (tokencode == TBaseType.lexnewline)
530        {ret = ret +" newline"; }
531      else
532        {ret = ret +" "+ getAstext();}
533      return ret;
534    }
535
536    /**
537     * Space, return, comments are treated as non-solid token by default
538     *
539     * @param tokentype token type
540     * @return true if token type is not one of ttwhitespace,ttreturn,ttsimplecomment,ttbracketedcomment
541     */
542    public  static boolean isnonsolidtoken(ETokenType tokentype){
543        return ( (tokentype == ETokenType.ttwhitespace) || (tokentype == ETokenType.ttreturn)
544                ||(tokentype == ETokenType.ttsimplecomment)||(tokentype == ETokenType.ttbracketedcomment));
545    }
546
547    /**
548     *  Is this token a solid token or not.
549     *
550     * @return true if it's a non-solid token.
551     */
552    public  boolean isnonsolidtoken(){
553        return !issolidtoken();
554    }
555
556    /**
557     * Is this token a non-solid token or not.
558     *
559     * @return true if it's a solid token.
560     */
561    public boolean issolidtoken(){
562        return !isnonsolidtoken(this.tokentype);
563    }
564
565    private TSourceTokenList tokensBefore = null;
566    private TSourceTokenList tokensAfter = null;
567
568    /**
569     * Used in sql formatter package only.
570     *
571     * @return source token list
572     */
573    public TSourceTokenList getTokensAfter() {
574        if (this.tokensAfter == null){
575            this.tokensAfter = new TSourceTokenList();
576        }
577        return tokensAfter;
578    }
579
580    /**
581     * Used in sql formatter package only
582     *
583     * @return source token list
584     */
585    public TSourceTokenList getTokensBefore() {
586        if (this.tokensBefore == null){
587            this.tokensBefore = new TSourceTokenList();
588        }
589        return tokensBefore;
590    }
591
592    private TSourceToken replaceToken = null;
593
594    /**
595     * Used in sql formatter package only
596     *
597     * @param replaceToken replaced token
598     */
599    public void setReplaceToken(TSourceToken replaceToken) {
600        this.replaceToken = replaceToken;
601    }
602
603    /**
604     * Used in sql formatter package only
605     *
606     * @return replaced token
607     */
608    public TSourceToken getReplaceToken() {
609
610        return replaceToken;
611    }
612
613    public  TSourceToken nextToken(){
614        TSourceToken ret = null;
615        if (this.container == null) return ret;
616        if (this.posinlist >= this.container.size() - 1) return null;
617        return this.container.get(this.posinlist+1);
618    }
619
620
621    public TSourceToken searchTokenAtTheEndOfSameLine(){
622        TSourceToken ret = null;
623        if (this.container == null) return ret;
624        int i = this.container.searchLastTokenAtTheSameLine(this.posinlist);
625        if (i == -1) return null;
626        return this.container.get(i);
627    }
628    /**
629     * Search a token before or after this token in the same source token list.
630     * The result token should has the tokencode equals to the targetTokenCode
631     * in a specified range.
632     *
633     * @param targetTokenCode, the token code need to be searched
634     * @param range, &gt; 0, search token start from the next token and forward,
635     * = 0, just compare with this token,
636     * &lt; 0, search from the previous token and backword.
637     * @return the token with the same token code, otherwise, return null.
638     */
639    public TSourceToken searchToken(int targetTokenCode,int range, int stopTokenCode, boolean stopAtSemiColon){
640        TSourceToken ret = null;
641        if (this.container == null) return ret;
642        return this.container.searchToken(targetTokenCode,"",this,range,stopTokenCode,stopAtSemiColon);
643    }
644
645    public TSourceToken searchToken(int targetTokenCode,int range){
646        return searchToken(targetTokenCode,range,0,false);
647    }
648
649    /**
650     * Search a token before or after this token in the same source token list.
651     * The result token should has the string text equals to the targetTokenText
652     * in a specified range.
653     *
654     * @param targetTokenText, the target string text
655     * @param range, &gt; 0, search token start from the next token and forward,
656     * = 0, just compare with this token,
657     * &lt; 0, search from the previous token and backword.
658     * @return the token with the same token code, otherwise, return null.
659     */
660    public TSourceToken searchToken(String targetTokenText,int range, int stopTokenCode, boolean stopAtSemiColon){
661        TSourceToken ret = null;
662        if (this.container == null) return ret;
663        return this.container.searchToken(0,targetTokenText,this,range,stopTokenCode,stopAtSemiColon);
664    }
665
666    public TSourceToken searchToken(String targetTokenText,int range) {
667            return searchToken(targetTokenText,range,0,false);
668    }
669    /**
670     * Search the first non-solid token after the next objectName.
671     * <p></p>
672     * Take this SQL for example:
673     * <pre>
674     * return new scott.func(x1);
675     * </pre>
676     * If this token is <code>new</code>, then call searchTokenAfterObjectName will return <code>(</code> token.
677     *
678     * @return solid token after the next objectName
679     */
680    public TSourceToken searchTokenAfterObjectName(){
681        TSourceToken ret = null;
682        if (this.container == null) return ret;
683        int i = container.nextObjectNameToken(posinlist,1,false);
684        if (i == -1) return  ret;
685        return container.get(i).nextSolidToken();
686    }
687
688    public  TSourceToken nextSolidToken(int pstep){
689        TSourceToken ret = null;
690        if (this.container == null) return ret;
691        return this.container.nextsolidtoken(this,pstep,false);
692    }
693
694
695    /**
696     * The next token whose {@link #tokentype} is not ttreturn,ttwhitespace,ttsimplecomment and ttbracketedcomment.
697     *
698     * @return the next solid token, returns null if not found.
699     */
700    public  TSourceToken nextSolidToken(){
701        TSourceToken ret = null;
702        if (this.container == null) return ret;
703        return this.container.nextsolidtoken(this,1,false);
704    }
705
706    /**
707     * The next token whose {@link #tokentype} is not ttreturn,ttwhitespace,ttsimplecomment and ttbracketedcomment.
708     *
709     * @param treatCommentAsSolidToken, set to true will treat comment token as a solid token
710     * @return the solid token if found, otherwise, return null
711     */
712    public  TSourceToken nextSolidToken(boolean treatCommentAsSolidToken){
713        TSourceToken ret = null;
714        if (this.container == null) return ret;
715        return this.container.nextsolidtoken(this,1,treatCommentAsSolidToken);
716    }
717
718
719    /**
720     * The previous token whose {@link #tokentype} is not ttreturn,ttwhitespace,ttsimplecomment and ttbracketedcomment.
721     *
722     * @return the solid token if found, otherwise, return null
723     */
724    public  TSourceToken prevSolidToken(){
725        TSourceToken ret = null;
726        if (this.container == null) return ret;
727        return this.container.nextsolidtoken(this,-1,false);
728    }
729
730    /**
731     * The previous token whose {@link #tokentype} is not ttreturn,ttwhitespace,ttsimplecomment and ttbracketedcomment
732     *
733     * @param treatCommentAsSolidToken set to true will treat comment token as a solid token
734     * @return the solid token if found, otherwise, return null
735     */
736    public  TSourceToken prevSolidToken(boolean treatCommentAsSolidToken){
737        TSourceToken ret = null;
738        if (this.container == null) return ret;
739        return this.container.nextsolidtoken(this,-1,treatCommentAsSolidToken);
740    }
741
742    /**
743     * Check to see if this token is the first token in a line of the input SQL
744     *
745     * @return true if this is the first token of a line, otherwise, return false
746     */
747    public boolean isFirstTokenOfLine(){
748        TSourceToken st = prevSolidToken();
749        if (st == null) return true;
750        return  (st.lineNo != this.lineNo);
751    }
752
753    /**
754     * Check to see if this token is the last token of in a line in the input SQL
755     *
756     * @return true if this is the last token of a line, otherwise, return false
757     */
758    public boolean isLastTokenOfLine(){
759        TSourceToken st = nextSolidToken();
760        if (st == null) return true;
761        return  (st.lineNo != this.lineNo);
762    }
763
764
765    private TSourceToken linkToken = null;
766
767    /**
768     * Create a link between two tokens. Make it easy to access another linked token.
769     * Usually, those are two tokens like the parenthesis in this SQL: <code>(select * from t)</code>
770     *
771     * @param linkToken the token need to be linked
772     */
773    public void setLinkToken(TSourceToken linkToken) {
774        this.linkToken = linkToken;
775        linkToken.linkToken = this;
776    }
777
778    /**
779     * Gets the linked token.
780     * Take this SQL for example:
781     * <code>(select * from t)</code>, if this token is '(', then you call this method will return ')' token.
782     *
783     * @return the paired parenthesis in select, expression
784     */
785    public TSourceToken getLinkToken() {
786
787        return linkToken;
788    }
789
790    /**
791     * Remove double quote <code>""</code>, bracket quote <code>[]</code> , left/right brace <code>{}</code>
792     * from a delimited identifier and return string text of this identifier.
793     *
794     * @return string text of this token
795     *
796     * @deprecated since 2.5.3.4
797     */
798    public String toUnQuotedString(){
799        if ((tokentype == ETokenType.ttdqstring)
800                ||(tokentype == ETokenType.ttdbstring)
801                ||(tokentype == ETokenType.ttbrstring)
802        ){
803                  return toString().substring(1,toString().length() - 1);
804        }else if ((dbvendor == EDbVendor.dbvmysql)&&(toString().startsWith("`"))){
805            return toString().substring(1,toString().length() - 1);
806        }else {
807            return toString();
808        }
809    }
810
811    /**
812     * Text representation for this token.
813     */
814    public String getAstext() {
815        return  toString(); //astext;
816    }
817
818    public void setAstext(String astext) {
819        this.astext = astext;
820    }
821}