001    package railo.transformer.util;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.util.ArrayList;
006    
007    import railo.commons.io.IOUtil;
008    import railo.commons.io.SystemUtil;
009    import railo.commons.lang.ClassUtil;
010    import railo.runtime.SourceFile;
011    
012    /**
013     * this class is a Parser String optimized for the transfomer (CFML Parser)
014     */
015    public final class CFMLString {
016    
017            /**
018             * Mindestens einen Space
019             */
020            public static final short AT_LEAST_ONE_SPACE=0;
021            
022            /**
023             * Mindestens ein Space
024             */
025            public static final short ZERO_OR_MORE_SPACE=1;
026            
027            /**
028             * Field <code>pos</code>
029             */
030            protected int pos=0;
031            /**
032             * Field <code>text</code>
033             */
034            protected char[] text;
035            /**
036             * Field <code>lcText</code>
037             */
038            protected char[] lcText;
039            /**
040             * Field <code>lines</code>
041             */
042            protected Integer[] lines;// TODO to int[]
043            /**
044             * Field <code>file</code>
045             */
046            protected SourceFile sf;
047    
048            private String charset;
049    
050            private boolean writeLog;
051    
052            private String source;
053            
054            private static final String NL=System.getProperty("line.separator");
055    
056            
057            
058            public CFMLString(SourceFile sf,String charset,boolean writeLog) throws IOException {
059                    this.writeLog=writeLog;
060                    this.charset=charset;
061                    this.sf=sf;
062                    this.source=sf.getPhyscalFile().getAbsolutePath();
063                    String content;
064                    InputStream is=null;
065                    try {
066                            is = IOUtil.toBufferedInputStream(sf.getPhyscalFile().getInputStream());
067                            if(ClassUtil.isBytecode(is))throw new AlreadyClassException(sf.getPhyscalFile());
068                            content=IOUtil.toString(is,charset);
069                            
070                    }
071                    finally {
072                            IOUtil.closeEL(is);
073                    }
074                    init(content.toString().toCharArray());
075            }
076    
077            /**
078             * Constructor of the class
079             * in this case source file is just for information
080             * @param text
081             * @param charset
082             * @param writeLog
083             * @param sf
084             */
085            public CFMLString(String text,String charset,boolean writeLog,SourceFile sf) {
086                    init(text.toCharArray());
087                    this.charset=charset;
088                    this.writeLog=writeLog;
089                    this.sf=sf;
090            }
091    
092            /**
093             * Constructor of the class
094             * @param text
095             * @param charset
096             */
097            public CFMLString(String text,String charset) {
098                    init(text.toCharArray());
099                    this.charset=charset;
100                    this.writeLog=false;
101            }
102            
103            /**
104             * initialize the CFMLString, used by all constructors
105             * @param text
106             */
107            protected void init(char[] text) {
108                    this.text=text;
109                    lcText=new char[text.length];
110                    
111                    ArrayList<Integer> arr=new ArrayList<Integer>();
112                    
113                    for(int i=0;i<text.length;i++) {
114                            pos=i;
115                            if(text[i]=='\n') {
116                                    arr.add(new Integer(i));
117                                    lcText[i]=' ';
118                            }
119                            else if(text[i]=='\r') {
120                                    if(isNextRaw('\n')){
121                                            lcText[i++]=' ';
122                                    }
123                                    arr.add(new Integer(i));
124                                    lcText[i]=' ';
125                            }
126                            else if(text[i]=='\t') lcText[i]=' ';
127                            else lcText[i]=Character.toLowerCase(text[i]);
128                    }
129                    pos=0;
130                    arr.add(new Integer(text.length));
131                    lines=(Integer[])arr.toArray(new Integer[arr.size()]);
132            }
133    
134            /**
135             * returns if the internal pointer is not on the last positions
136             */
137            public boolean hasNext()  {
138                    return pos+1<lcText.length;
139            }
140    
141            /**
142             * moves the internal pointer to the next position, no check if the next position is still valid
143            */
144            public void next(){
145                    pos++;
146            }
147            /**
148             * moves the internal pointer to the previous position, no check if the next position is still valid
149             */
150            public void previous(){
151                    pos--;
152            }
153    
154            /**
155             * returns the character of the current position of the internal pointer
156             */
157            public char getCurrent() {
158                    return text[pos];
159            }
160            
161            /**
162             * returns the lower case representation of the character of the current position
163             */
164            public char getCurrentLower() {
165                    return lcText[pos];
166            }
167    
168            /**
169             * returns the character at the given position
170             */
171            public char charAt(int pos) {
172                    return text[pos];
173            }
174            
175            /**
176             * returns the character at the given position as lower case representation
177             */
178            public char charAtLower(int pos) {
179                    return lcText[pos];
180            }
181    
182            /**
183             * is the character at the next position the same as the character provided by the input parameter
184             */
185            public boolean isNext(char c) {
186                    if(!hasNext()) return false;
187                    return lcText[pos+1]==c;
188            }
189            
190            private boolean isNextRaw(char c) {
191                    if(!hasNext()) return false;
192                    return text[pos+1]==c;
193            }
194            
195            
196            /**
197             * is the character at the current position (internal pointer) in the range of the given input characters?
198             * @param left lower value.
199             * @param right upper value.
200             */
201            public boolean isCurrentBetween(char left, char right) {
202                    if(!isValidIndex()) return false;
203                    return lcText[pos]>=left && lcText[pos]<=right;
204            }
205    
206            
207            /**
208             * returns if the character at the current position (internal pointer) is a valid variable character
209             */
210            public boolean isCurrentVariableCharacter() {
211            if(!isValidIndex()) return false;
212            return isCurrentLetter() || isCurrentNumber() || isCurrent('$') || isCurrent('_');
213        }
214            
215        /**
216         * returns if the current character is a letter (a-z,A-Z)
217         * @return is a letter
218         */
219        public boolean isCurrentLetter() {
220            if(!isValidIndex()) return false;
221            return lcText[pos]>='a' && lcText[pos]<='z';
222        }
223    
224        /**
225         * returns if the current character is a number (0-9)
226         * @return is a letter
227         */
228        public boolean isCurrentNumber() {
229            if(!isValidIndex()) return false;
230            return lcText[pos]>='0' && lcText[pos]<='9';
231        }
232        
233        
234        /**
235         * retuns if the current character (internal pointer) is a valid special sign (_, $, Pound Symbol, Euro Symbol)
236         */
237        public boolean isCurrentSpecial() {
238            if(!isValidIndex()) return false;
239            return lcText[pos]=='_' || lcText[pos]=='$' || lcText[pos]==SystemUtil.CHAR_EURO || lcText[pos]==SystemUtil.CHAR_POUND;
240        }
241            
242            /**
243             * is the current character (internal pointer) the same as the given
244             */
245            public boolean isCurrent(char c) {
246                    if(!isValidIndex()) return false;
247                    return lcText[pos]==c;
248            }
249            
250            /**
251             * forward the internal pointer plus one if the next character is the same as the given input
252             */
253            public boolean forwardIfCurrent(char c) {
254                    if(isCurrent(c)) {
255                            pos++;
256                            return true;
257                    } 
258                    return false;
259            }
260            
261            /**
262             * returns if the current character (internal pointer) and the following are the same as the given input
263             */
264            public boolean isCurrent(String str) {
265                    if(pos+str.length()>lcText.length) return false;
266                    for(int i=str.length()-1;i>=0;i--)   {
267                            if(str.charAt(i)!=lcText[pos+i]) return false;
268                    }
269                    return true;
270                    /*char[] c=str.toCharArray();
271                    // @ todo not shure for length
272                    if(pos+c.length>text.length) return false;
273                    for(int i=c.length-1;i>=0;i--)       {
274                            if(c[i]!=lcText[pos+i]) return false;
275                    }
276                    return true;*/                  
277            }
278            
279            /**
280             * forwards if the current character (internal pointer) and the following are the same as the given input
281             */
282            public boolean forwardIfCurrent(String str) {
283                    boolean is=isCurrent(str);
284                    if(is)pos+=str.length();
285                    return is;
286            }
287            
288            /**
289             * @param str string to check against current position
290             * @param startWithSpace if true there must be whitespace at the current position
291             * @return does the criteria match?
292             */
293            public boolean forwardIfCurrent(String str, boolean startWithSpace) {
294                    if(!startWithSpace) return forwardIfCurrent(str);
295                    
296                    int start=pos;
297                    if(!removeSpace())return false;
298                    
299                    if(!forwardIfCurrent(str)){
300                            pos=start;
301                            return false;
302                    }
303                    return true;
304            }
305            
306            /**
307             * @param str string to check against current position
308             * @param startWithSpace if true there must be whitespace at the current position
309             * @param followedByNoVariableCharacter the character following the string must be a none variable character (!a-z,A-Z,0-9,_$) (not eaten)
310             * @return does the criteria match?
311             */
312            public boolean forwardIfCurrent(String str, boolean startWithSpace, boolean followedByNoVariableCharacter) {
313                    
314                    int start=pos;
315                    if(startWithSpace && !removeSpace())return false;
316                    
317                    if(!forwardIfCurrent(str)){
318                            pos=start;
319                            return false;
320                    }
321                    if(followedByNoVariableCharacter && isCurrentVariableCharacter()) {
322                            pos=start;
323                            return false;
324                    }
325                    return true;
326            }
327            
328            
329            
330            
331    
332            /**
333             * forwards if the current character (internal pointer) and the following are the same as the given input, followed by a none word character
334             */
335            public boolean forwardIfCurrentAndNoWordAfter(String str) {
336                    int c=pos;
337                    if(forwardIfCurrent(str)) {
338                            if(!isCurrentBetween('a','z') && !isCurrent('_'))return true;
339                    }
340                    pos=c;
341                    return false;
342            }
343            
344            /**
345             * forwards if the current character (internal pointer) and the following are the same as the given input, followed by a none word character or a number
346             */
347            public boolean forwardIfCurrentAndNoVarExt(String str) {
348                    int c=pos;
349                    if(forwardIfCurrent(str)) {
350                            if(!isCurrentBetween('a','z') &&!isCurrentBetween('0','9') && !isCurrent('_'))return true;
351                    }
352                    pos=c;
353                    return false;
354            }
355            
356            /**
357             * Gibt zur�ck ob first den folgenden Zeichen entspricht, gefolgt von Leerzeichen und second.
358             * @param first Erste Zeichen zum Vergleich (Vor den Leerzeichen).
359             * @param second Zweite Zeichen zum Vergleich (Nach den Leerzeichen).
360             * @return Gibt zur�ck ob die eingegebenen Werte dem Inhalt beim aktuellen Stand des Zeigers entsprechen.
361             */
362            public boolean isCurrent(String first,char second) {
363                    int start=pos;
364                    if(!forwardIfCurrent(first)) return false; 
365                    removeSpace();
366                    boolean rtn=isCurrent(second); 
367                    pos=start;
368                    return rtn;                     
369            }
370            
371            /**
372             * Gibt zur�ck ob first den folgenden Zeichen entspricht, gefolgt von Leerzeichen und second.
373             * @param first Erstes Zeichen zum Vergleich (Vor den Leerzeichen).
374             * @param second Zweites Zeichen zum Vergleich (Nach den Leerzeichen).
375             * @return Gibt zur�ck ob die eingegebenen Werte dem Inhalt beim aktuellen Stand des Zeigers entsprechen.
376             */
377            public boolean isCurrent(char first,char second) {
378                    int start=pos;
379                    if(!forwardIfCurrent(first)) return false; 
380                    removeSpace();
381                    boolean rtn=isCurrent(second); 
382                    pos=start;
383                    return rtn;                     
384            }
385            
386            /**
387             * Gibt zur�ck ob first den folgenden Zeichen entspricht, 
388             * gefolgt von Leerzeichen und second,
389             * wenn ja wird der Zeiger um die L�nge der �bereinstimmung nach vorne gestellt.
390             * @param first Erste Zeichen zum Vergleich (Vor den Leerzeichen).
391             * @param second Zweite Zeichen zum Vergleich (Nach den Leerzeichen).
392             * @return Gibt zur�ck ob der Zeiger vorw�rts geschoben wurde oder nicht.
393             */
394            public boolean forwardIfCurrent(String first,char second) {
395                    int start=pos;
396                    if(!forwardIfCurrent(first)) return false; 
397                    removeSpace();
398                    boolean rtn=forwardIfCurrent(second); 
399                    if(!rtn)pos=start;
400                    return rtn;     
401            }
402            
403            /**
404             * Gibt zur�ck ob ein Wert folgt und vor und hinterher Leerzeichen folgen.
405             * @param before Definition der Leerzeichen vorher.
406             * @param val Gefolgter Wert der erartet wird.
407             * @param after Definition der Leerzeichen nach dem Wert.
408             * @return Gibt zur�ck ob der Zeiger vorw�rts geschoben wurde oder nicht.
409             */
410            public boolean forwardIfCurrent(short before, String val,short after) {
411                    int start=pos;
412                    // space before
413                    if(before==AT_LEAST_ONE_SPACE) {
414                            if(!removeSpace()) return false;
415                    }
416                    else removeSpace();
417                    
418                    // value
419                    if(!forwardIfCurrent(val)) {
420                            setPos(start);
421                            return false;
422                    }
423                    
424                    // space after
425                    if(after==AT_LEAST_ONE_SPACE) {
426                            if(!removeSpace()) { 
427                                    setPos(start);
428                                    return false; 
429                            } 
430                    }
431                    else removeSpace();
432                    return true;
433            }
434            
435            /**
436             * Gibt zur�ck ob first den folgenden Zeichen entspricht, 
437             * gefolgt von Leerzeichen und second,
438             * wenn ja wird der Zeiger um die L�nge der �bereinstimmung nach vorne gestellt.
439             * @param first Erste Zeichen zum Vergleich (Vor den Leerzeichen).
440             * @param second Zweite Zeichen zum Vergleich (Nach den Leerzeichen).
441             * @return Gibt zur�ck ob der Zeiger vorw�rts geschoben wurde oder nicht.
442             */
443            public boolean forwardIfCurrent(char first,char second) {
444                    int start=pos;
445                    if(!forwardIfCurrent(first)) return false; 
446                    removeSpace();
447                    boolean rtn=forwardIfCurrent(second); 
448                    if(!rtn)pos=start;
449                    return rtn;     
450            }
451            
452            /**
453             * Gibt zur�ck ob first den folgenden Zeichen entspricht, gefolgt von Leerzeichen und second.
454             * @param first Erste Zeichen zum Vergleich (Vor den Leerzeichen).
455             * @param second Zweite Zeichen zum Vergleich (Nach den Leerzeichen).
456             * @return Gibt zur�ck ob die eingegebenen Werte dem Inhalt beim aktuellen Stand des Zeigers entsprechen.
457             */
458            public boolean isCurrent(String first,String second) {
459                    int start=pos;
460                    if(!forwardIfCurrent(first)) return false; 
461                    removeSpace();
462                    boolean rtn=isCurrent(second); 
463                    pos=start;
464                    return rtn;                     
465            }
466            
467            /**
468             * Gibt zur�ck ob first den folgenden Zeichen entspricht, 
469             * gefolgt von Leerzeichen und second,
470             * wenn ja wird der Zeiger um die L�nge der �bereinstimmung nach vorne gestellt.
471             * @param first Erste Zeichen zum Vergleich (Vor den Leerzeichen).
472             * @param second Zweite Zeichen zum Vergleich (Nach den Leerzeichen).
473             * @return Gibt zur�ck ob der Zeiger vorw�rts geschoben wurde oder nicht.
474             */
475            public boolean forwardIfCurrent(String first,String second) {
476                    int start=pos;
477                    if(!forwardIfCurrent(first)) return false; 
478                    if(!removeSpace()){
479                            pos=start;
480                            return false;
481                    }
482                    boolean rtn=forwardIfCurrent(second); 
483                    if(!rtn)pos=start;
484                    return rtn;     
485            }
486            
487            public boolean forwardIfCurrent(String first,String second,String third) {
488                    int start=pos;
489                    if(!forwardIfCurrent(first)) return false; 
490                    
491                    if(!removeSpace()){
492                            pos=start;
493                            return false;
494                    }
495                    
496                    if(!forwardIfCurrent(second)){
497                            pos=start;
498                            return false;
499                    }
500                    
501                    if(!removeSpace()){
502                            pos=start;
503                            return false;
504                    }
505                    
506                    boolean rtn=forwardIfCurrent(third); 
507                    if(!rtn)pos=start;
508                    return rtn;     
509            }
510    
511            public boolean forwardIfCurrent(String first,String second,String third, boolean startWithSpace) {
512                    if(!startWithSpace) return forwardIfCurrent(first, second, third);
513                    int start=pos;
514                    
515                    if(!removeSpace())return false;
516                    
517                    if(!forwardIfCurrent(first,second,third)){
518                            pos=start;
519                            return false;
520                    }
521                    return true;    
522            }
523            
524            public boolean forwardIfCurrent(String first,String second,String third, boolean startWithSpace, boolean followedByNoVariableCharacter) {
525                    int start=pos;
526                    
527                    if(startWithSpace && !removeSpace())return false;
528                    
529                    if(!forwardIfCurrent(first,second,third)){
530                            pos=start;
531                            return false;
532                    }
533                    if(followedByNoVariableCharacter && isCurrentVariableCharacter()) {
534                            pos=start;
535                            return false;
536                    }
537                    return true;    
538            }
539            
540            
541            public boolean forwardIfCurrent(String first,String second, boolean startWithSpace, boolean followedByNoVariableCharacter) {
542                    int start=pos;
543                    
544                    if(startWithSpace && !removeSpace())return false;
545                    
546                    if(!forwardIfCurrent(first,second)){
547                            pos=start;
548                            return false;
549                    }
550                    if(followedByNoVariableCharacter && isCurrentVariableCharacter()) {
551                            pos=start;
552                            return false;
553                    }
554                    return true;    
555            }
556            
557            
558            
559            public boolean forwardIfCurrent(String first,String second,String third, String forth) {
560                    int start=pos;
561                    if(!forwardIfCurrent(first)) return false; 
562                    
563                    if(!removeSpace()){
564                            pos=start;
565                            return false;
566                    }
567                    
568                    if(!forwardIfCurrent(second)){
569                            pos=start;
570                            return false;
571                    }
572                    
573                    if(!removeSpace()){
574                            pos=start;
575                            return false;
576                    }
577                    
578                    
579                    if(!forwardIfCurrent(third)){
580                            pos=start;
581                            return false;
582                    }
583                    
584                    if(!removeSpace()){
585                            pos=start;
586                            return false;
587                    }
588                    
589                    boolean rtn=forwardIfCurrent(forth); 
590                    if(!rtn)pos=start;
591                    return rtn;     
592                    
593            }
594            
595            
596            /**
597             * Gibt zur�ck ob sich vor dem aktuellen Zeichen Leerzeichen befinden.
598             * @return Gibt zur�ck ob sich vor dem aktuellen Zeichen Leerzeichen befinden.
599             */
600            public boolean hasSpaceBefore() {
601                    return pos > 0 && lcText[pos - 1] == ' ';
602            }
603            
604            public boolean hasNLBefore() {
605                    int index=0;
606                    while(pos-(++index) >= 0){
607                            if(text[pos - index] == '\n')return true;
608                            if(text[pos - index] == '\r')return true;
609                            if(lcText[pos - index] != ' ') return false;
610                    }
611                    return false;
612            }
613            
614            /**
615             * Stellt den Zeiger nach vorne, wenn er sich innerhalb von Leerzeichen befindet, 
616             * bis die Leerzeichen fertig sind. 
617             * @return Gibt zur�ck ob der Zeiger innerhalb von Leerzeichen war oder nicht.
618             */
619            public boolean removeSpace() {
620                    int start=pos;
621                    while(pos<lcText.length && lcText[pos]==' ') {
622                            pos++;
623                    }
624                    return (start<pos);
625            }
626            public String removeAndGetSpace() {
627                    int start=pos;
628                    while(pos<lcText.length && lcText[pos]==' ') {
629                            pos++;
630                    }
631                    return substring(start,pos-start);
632            }
633            
634            /**
635             * Stellt den internen Zeiger an den Anfang der n�chsten Zeile, 
636             * gibt zur�ck ob eine weitere Zeile existiert oder ob es bereits die letzte Zeile war.
637             * @return Existiert eine weitere Zeile.
638             */
639            public boolean nextLine() {
640                    while(isValidIndex() && text[pos]!='\n' && text[pos]!='\r') {
641                            next();
642                    }
643                    if(!isValidIndex()) return false;
644                    
645                    if(text[pos]=='\n') {
646                            next();
647                            return isValidIndex();
648                    }
649                    if(text[pos]=='\r') {
650                            next();
651                            if(isValidIndex() && text[pos]=='\n') {
652                                    next();
653                            }
654                            return isValidIndex();
655                    }
656                    return false;
657            }
658            
659            /**
660             * Gibt eine Untermenge des CFMLString als Zeichenkette zur�ck, 
661             * ausgehend von start bis zum Ende des CFMLString.
662             * @param start Von wo aus die Untermege ausgegeben werden soll.
663             * @return Untermenge als Zeichenkette
664             */
665            public String substring(int start) {
666                    return substring(start,lcText.length-start);
667            }
668    
669            /**
670             * Gibt eine Untermenge des CFMLString als Zeichenkette zur�ck, 
671             * ausgehend von start mit einer maximalen L�nge count.
672             * @param start Von wo aus die Untermenge ausgegeben werden soll.
673             * @param count Wie lange die zur�ckgegebene Zeichenkette maximal sein darf.
674             * @return Untermenge als Zeichenkette.
675             */
676            public String substring(int start, int count) {
677                    return String.valueOf(text,start,count);
678            }
679    
680            /**
681             * Gibt eine Untermenge des CFMLString als Zeichenkette in Kleinbuchstaben zur�ck, 
682             * ausgehend von start bis zum Ende des CFMLString.
683             * @param start Von wo aus die Untermenge ausgegeben werden soll.
684             * @return  Untermenge als Zeichenkette in Kleinbuchstaben.
685             */
686            public String substringLower(int start) {
687                    return substringLower(start,lcText.length-start);
688            }
689    
690            /**
691             * Gibt eine Untermenge des CFMLString als Zeichenkette in Kleinbuchstaben zur�ck, 
692             * ausgehend von start mit einer maximalen L�nge count.
693             * @param start Von wo aus die Untermenge ausgegeben werden soll.
694             * @param count Wie lange die zur�ckgegebene Zeichenkette maximal sein darf.
695             * @return  Untermenge als Zeichenkette in Kleinbuchstaben.
696             */
697            public String substringLower(int start, int count) {
698                    return String.valueOf(lcText,start,count);
699            }
700            
701            /**
702             * Gibt eine Untermenge des CFMLString als CFMLString zur�ck, 
703             * ausgehend von start bis zum Ende des CFMLString.
704             * @param start Von wo aus die Untermenge ausgegeben werden soll.
705             * @return Untermenge als CFMLString
706             */
707            public CFMLString subCFMLString(int start) {
708                    return subCFMLString(start,text.length-start);
709            }
710            
711            /**
712            * Gibt eine Untermenge des CFMLString als CFMLString zur�ck, 
713            * ausgehend von start mit einer maximalen L�nge count.
714            * @param start Von wo aus die Untermenge ausgegeben werden soll.
715            * @param count Wie lange die zur�ckgegebene Zeichenkette maximal sein darf.
716            * @return Untermenge als CFMLString
717            */
718       public CFMLString subCFMLString(int start, int count) {
719                    return new CFMLString(String.valueOf(text,start,count),charset,writeLog,sf);
720                    
721       }
722            
723            /** Gibt den CFMLString als String zur�ck.
724             * @see java.lang.Object#toString()
725             */
726            public String toString() {
727                    return new String(this.text);
728            }
729            
730            /**
731             * Gibt die aktuelle Position des Zeigers innerhalb des CFMLString zur�ck.
732             * @return Position des Zeigers
733             */
734            public int getPos() {
735                    return pos;
736            }
737            
738            /**
739             * Setzt die Position des Zeigers innerhalb des CFMLString, ein ung�ltiger index wird ignoriert.
740              * @param pos Position an die der Zeiger gestellt werde soll.
741             */
742            public void setPos(int pos) {
743                    this.pos= pos;
744            }
745            
746            /**
747             * Gibt die aktuelle Zeile zur�ck in der der Zeiger des CFMLString steht.
748             * @return Zeilennummer
749             */
750            public int getLine() {
751                    return getLine(pos);
752            }
753            
754            /**
755             * Gibt zur�ck in welcher Zeile die angegebene Position ist.
756             * @param pos Position von welcher die Zeile erfragt wird
757             * @return Zeilennummer
758             */
759            public int getLine(int pos) {
760                    for(int i=0;i<lines.length;i++) {
761                            if(pos<=lines[i].intValue())
762                            return i+1;
763                    }
764                    return lines.length;
765            }
766            
767            /**
768             * Gibt die Stelle in der aktuelle Zeile zur�ck, in welcher der Zeiger steht.
769             * @return Position innerhalb der Zeile.
770             */
771            public int getColumn() {
772                    return getColumn(pos);
773            }
774            
775            /**
776             * Gibt die Stelle in der Zeile auf die pos zeigt zur�ck.
777             * @param pos Position von welcher die Zeile erfragt wird
778             * @return Position innerhalb der Zeile.
779             */
780            public int getColumn(int pos) {
781                    int line=getLine(pos)-1;
782                    if(line==0) return pos+1;
783                    return pos-lines[line-1].intValue();
784            }
785            
786            /**
787             * Gibt die Zeile auf welcher der Zeiger steht als String zur�ck.
788             * @return Zeile als Zeichenkette
789             */
790            public String getLineAsString() {
791                    return getLineAsString(getLine(pos));
792            }
793            /**
794             * Gibt die angegebene Zeile als String zur�ck.
795             * @param line Zeile die zur�ck gegeben werden soll
796             * @return Zeile als Zeichenkette
797             */
798            public String getLineAsString(int line) {
799                    int index=line-1;
800                    if(lines.length<=index) return null;
801                    int max=lines[index].intValue();
802                    int min=0;
803                    if(index!=0)
804                            min=lines[index-1].intValue()+1;
805                    
806                    if(min<max && max-1<lcText.length)
807                            return this.substring(min, max-min);
808                    return "";
809            }
810    
811            /**
812             * Gibt zur�ck ob der Zeiger auf dem letzten Zeichen steht.
813             * @return Gibt zur�ck ob der Zeiger auf dem letzten Zeichen steht.
814             */
815            public boolean isLast() {
816                    return pos==lcText.length-1;
817            }
818            
819            /**
820             * Gibt zur�ck ob der Zeiger nach dem letzten Zeichen steht.
821             * @return Gibt zur�ck ob der Zeiger nach dem letzten Zeichen steht.
822             */
823            public boolean isAfterLast() {
824                    return pos>=lcText.length;
825            }
826            /**
827             * Gibt zur�ck ob der Zeiger einen korrekten Index hat.
828             * @return Gibt zur�ck ob der Zeiger einen korrekten Index hat.
829             */
830            public boolean isValidIndex() {
831                    return pos<lcText.length && pos>-1;
832            }
833            
834            /**
835             * Gibt zur�ck, ausgehend von der aktuellen Position, 
836             * wann das n�chste Zeichen folgt das gleich ist wie die Eingabe, 
837             * falls keines folgt wird �1 zur�ck gegeben. 
838             * Gross- und Kleinschreibung der Zeichen werden igoriert.
839             * @param c gesuchtes Zeichen
840             * @return Zeichen das gesucht werden soll.
841             */
842            public int indexOfNext(char c) {
843                    for(int i=pos;i<lcText.length;i++) {
844                            if(lcText[i]==c) return i;
845                    }
846                    return -1;
847            }
848            
849            /**
850             * Gibt das letzte Wort das sich vor dem aktuellen Zeigerstand befindet zur�ck, 
851             * falls keines existiert wird null zur�ck gegeben.
852             * @return Word vor dem aktuellen Zeigerstand.
853             */
854            public String lastWord() {
855                    int size = 1;
856                    while (pos - size > 0 && lcText[pos - size] == ' ') {
857                            size++;
858                    }
859                    while (pos - size > 0
860                            && lcText[pos - size] != ' '
861                            && lcText[pos - size] != ';') {
862                            size++;
863                    }
864                    return this.substring((pos - size + 1), (pos - 1));
865            }
866    
867            /**
868             * Gibt die L�nge des CFMLString zur�ck.
869             * @return L�nge des CFMLString.
870             */
871            public int length() {
872                    return lcText.length;
873            }
874    
875            /**
876             * Gibt die Quelle aus dem der CFML Code stammt als File Objekt zur�ck, 
877             * falls dies nicht aud einem File stammt wird null zur�ck gegeben.
878             * @return source Quelle des CFML Code.
879             */
880            public SourceFile getSourceFile() {
881                    return sf;
882            }
883    
884            /**
885             * Pr�ft ob das �bergebene Objekt diesem Objekt entspricht.
886             * @param o Object zum vergleichen.
887             * @return Ist das �bergebene Objekt das selbe wie dieses.
888             */
889            public boolean equals(Object o) {
890                    if(!(o instanceof CFMLString))return false;
891                    return o.toString().equals(this.toString());
892            }
893    
894            public String getCharset() {
895                    return charset;
896            }
897    
898    
899            public boolean getWriteLog() {
900                    return writeLog;
901            }
902    
903    
904            public String getText() {
905                    return new String(text);
906            }
907            
908    
909    
910            /**
911             * @return the source
912             */
913            public String getSource() {
914                    return source;
915            }
916    }