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