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