1d502f3bcdd9edf4801dcacaddfc3f1c97090cea
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPPartitionScanner.java
1 /**
2  * This program and the accompanying materials
3  * are made available under the terms of the Common Public License v1.0
4  * which accompanies this distribution, and is available at
5  * http://www.eclipse.org/legal/cpl-v10.html
6  * Created on 05.03.2003
7  *
8  * @author Stefan Langer (musk)
9  * @version $Revision: 1.14 $
10  */
11 package net.sourceforge.phpeclipse.phpeditor.php;
12
13 import java.util.*;
14
15 import org.eclipse.jface.text.*;
16 import org.eclipse.jface.text.rules.*;
17
18 /**
19  * 
20  */
21 public class PHPPartitionScanner implements IPartitionTokenScanner {
22   private static final boolean DEBUG = false;
23   private IDocument fDocument = null;
24   private int fOffset = -1;
25   private String fContentType = IPHPPartitionScannerConstants.HTML;
26   private String fPrevContentType;
27
28   private boolean partitionBorder = false;
29   private int fTokenOffset;
30   private int fEnd = -1;
31   private int fLength;
32   private Map tokens = new HashMap();
33
34   public PHPPartitionScanner() {
35     this.tokens.put(
36       IPHPPartitionScannerConstants.PHP,
37       new Token(IPHPPartitionScannerConstants.PHP));
38     this.tokens.put(
39       IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT,
40       new Token(IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT));
41     this.tokens.put(
42       IPHPPartitionScannerConstants.HTML,
43       new Token(IPHPPartitionScannerConstants.HTML));
44     this.tokens.put(
45       IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT,
46       new Token(IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT));
47     this.tokens.put(
48       IDocument.DEFAULT_CONTENT_TYPE,
49       new Token(IDocument.DEFAULT_CONTENT_TYPE));
50   }
51
52   private IToken getToken(String type) {
53     fLength = fOffset - fTokenOffset;
54     IToken token = (IToken) this.tokens.get(type);
55     Assert.isNotNull(token, "Token for type \"" + type + "\" not found!");
56     if (DEBUG) {
57       System.out.println(
58         "Partition: fTokenOffset="
59           + fTokenOffset
60           + " fContentType="
61           + fContentType
62           + " fLength="
63           + fLength);
64     }
65     return token;
66   }
67
68 /* (non-Javadoc)
69  * @see org.eclipse.jface.text.rules.IPartitionTokenScanner#setPartialRange(org.eclipse.jface.text.IDocument, int, int, java.lang.String, int)
70  */
71 public void setPartialRange(
72     IDocument document,
73     int offset,
74     int length,
75     String contentType,
76     int partitionOffset)
77 {
78     this.setRange(document, offset, length);
79     if (DEBUG)
80     {
81         System.out.println(
82             "PartialRange: contentType="
83                 + contentType
84                 + " partitionOffset="
85                 + partitionOffset);
86     }
87         
88         if(offset == partitionOffset)
89         {
90                 try
91         {
92             fContentType = fDocument.getContentType(offset);
93         }
94         catch (BadLocationException e)
95         {
96             //should never happen
97         }
98         }
99     else if (this.tokens.containsKey(contentType))
100         fContentType = contentType;
101     // TODO Calculate previouse contenttype
102     if (partitionOffset > -1)
103     {
104         partitionBorder = false;
105         fTokenOffset = partitionOffset;
106     }
107 }
108
109   /* (non-Javadoc)
110    * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
111    */
112   public int getTokenLength() {
113     return fLength;
114   }
115
116   /* (non-Javadoc)
117    * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
118    */
119   public int getTokenOffset() {
120     return fTokenOffset;
121   }
122
123 /* (non-Javadoc)
124  * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
125  */
126 public IToken nextToken()
127 {
128     int c;
129
130     // check if we are not allready at the end of the
131     // file
132     if ((c = read()) == ICharacterScanner.EOF)
133     {
134         partitionBorder = false;
135         return Token.EOF;
136     }
137     else
138         unread();
139
140     if (partitionBorder)
141     {
142         fTokenOffset = fOffset;
143         partitionBorder = false;
144     }
145
146     while ((c = read()) != ICharacterScanner.EOF)
147     {
148         switch (c)
149         {
150             case '<' :
151                 if (fContentType
152                     != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT
153                     && checkPattern(new char[] { '?', 'p', 'h', 'p' }, true))
154                 {
155                     if (fContentType != IPHPPartitionScannerConstants.PHP
156                         && fOffset - 5 > 0)
157                     {
158                         fOffset -= 5;
159                         IToken token = getToken(fContentType);
160                         // save previouse contenttype
161                         fPrevContentType = fContentType;
162
163                         fContentType = IPHPPartitionScannerConstants.PHP;
164
165                         return token;
166                     }
167                     else
168                         fContentType = IPHPPartitionScannerConstants.PHP;
169
170                     // remember offset of this partition
171                     fTokenOffset = fOffset - 5;
172                 }
173                 else if (
174                     fContentType
175                         != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT
176                     && checkPattern(new char[] { '?' }, false))
177                 {
178                     if (fContentType != IPHPPartitionScannerConstants.PHP
179                         && fOffset - 2 > 0)
180                     {
181                         fOffset -= 2;
182                             IToken token = getToken(fContentType);
183                 // save previouse contenttype
184                         fPrevContentType =
185                         fContentType;
186                             fContentType = IPHPPartitionScannerConstants.PHP;
187                             return token;
188                             }
189                     else
190                         fContentType = IPHPPartitionScannerConstants.PHP;
191             // remember offset of this partition
192                         fTokenOffset =
193                     fOffset - 3;
194                             }
195                 else if (checkPattern(new char[] { '!', '-', '-' }))
196                 { // return previouse partition
197                 if (
198                     fContentType
199                         != IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT
200                     && fOffset - 4 > 0)
201                     {
202                         fOffset -= 4;
203                             IToken token = getToken(fContentType);
204                             fContentType =
205                                 IPHPPartitionScannerConstants
206                                     .HTML_MULTILINE_COMMENT;
207                             return token;
208                             }
209                     else
210                         fContentType =
211                             IPHPPartitionScannerConstants
212                                 .HTML_MULTILINE_COMMENT;
213                             fTokenOffset = fOffset - 4;
214                             }
215                 break; case '?' :
216                 if (fContentType == IPHPPartitionScannerConstants.PHP)
217                 {
218                     if ((c = read()) == '>')
219                     { // TODO Actually calculate the previouse contenttype from the document
220                     if (
221                         fPrevContentType
222                         != null)
223                             fContentType = fPrevContentType; else
224                                 fContentType =
225                                     IPHPPartitionScannerConstants.HTML;
226                                     partitionBorder = true;
227                                     return getToken(
228                                         IPHPPartitionScannerConstants.PHP);
229                                     }
230                     else if (c != ICharacterScanner.EOF)
231                         unread(); }
232                 break; case '-' :
233                 if (fContentType
234                     == IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT
235                     && checkPattern(new char[] { '-', '>' }))
236                 {
237                     fContentType = IPHPPartitionScannerConstants.HTML;
238                         partitionBorder = true;
239                         return getToken(
240                             IPHPPartitionScannerConstants
241                                 .HTML_MULTILINE_COMMENT);
242                         }
243                 break; case '/' :
244                 if ((c = read()) == '*')
245                 { // MULTINE COMMENT JAVASCRIPT, CSS, PHP
246                 if (
247                     fContentType == IPHPPartitionScannerConstants.PHP
248                     && fOffset - 2 > 0)
249                     {
250                         fOffset -= 2;
251                             IToken token = getToken(fContentType);
252                             fContentType =
253                                 IPHPPartitionScannerConstants
254                                     .PHP_MULTILINE_COMMENT;
255                             return token;
256                             }
257                     else if (
258                         fContentType
259                             == IPHPPartitionScannerConstants
260                                 .PHP_MULTILINE_COMMENT)
261                     {
262
263                         fTokenOffset = fOffset - 2; }
264
265                 }
266                 else if (c != ICharacterScanner.EOF)
267                     unread(); break; case '*' :
268                         if ((c = read()) == '/')
269                         {
270                             if (fContentType
271                                 == IPHPPartitionScannerConstants
272                                     .PHP_MULTILINE_COMMENT)
273                             {
274                                 fContentType =
275                                     IPHPPartitionScannerConstants.PHP;
276                                     partitionBorder = true;
277                                     return getToken(
278                                         IPHPPartitionScannerConstants
279                                             .PHP_MULTILINE_COMMENT);
280                                     }
281                             else if (
282                                 fContentType
283                                     == IPHPPartitionScannerConstants
284                                         .CSS_MULTILINE_COMMENT)
285                             {
286                             }
287                             else if (
288                                 fContentType
289                                     == IPHPPartitionScannerConstants
290                                         .JS_MULTILINE_COMMENT)
291                             {
292                             }
293                         }
294                         else if (c != ICharacterScanner.EOF)
295                             unread(); break; }
296     } // end of file reached but we have to return the
297     // last partition.
298     return getToken(fContentType);
299         }
300   /* (non-Javadoc)
301    * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(org.eclipse.jface.text.IDocument, int, int)
302    */
303   public void setRange(IDocument document, int offset, int length) {
304     if (DEBUG) {
305       System.out.println("SET RANGE: offset=" + offset + " length=" + length);
306     }
307
308     fDocument = document;
309     fOffset = offset;
310     fTokenOffset = offset;
311     fLength = 0;
312     fEnd = fOffset + length;
313     //partitionBorder = false;
314   }
315
316   private int read() {
317     try {
318       if (fOffset < fEnd) {
319         return fDocument.getChar(fOffset++);
320       }
321       return ICharacterScanner.EOF;
322     } catch (BadLocationException e) {
323       // should never happen
324       // TODO write stacktrace to log
325       fOffset = fEnd;
326       return ICharacterScanner.EOF;
327     }
328   }
329
330   private void unread() {
331     --fOffset;
332   }
333
334   private boolean checkPattern(char[] pattern) {
335     return checkPattern(pattern, false);
336   }
337
338   /**
339    * Check if next character sequence read from document is equals to 
340    * the provided pattern. Pattern is read from left to right until the 
341    * first character read doesn't match. If this happens all read characters are
342    * unread.
343    * @param pattern The pattern to check.
344    * @return <code>true</code> if pattern is equals else returns <code>false</code>.
345    */
346   private boolean checkPattern(char[] pattern, boolean ignoreCase) {
347     int prevOffset = fOffset;
348     for (int i = 0; i < pattern.length; i++) {
349       int c = read();
350
351       if (c == ICharacterScanner.EOF
352         || !letterEquals(c, pattern[i], ignoreCase)) {
353         fOffset = prevOffset;
354         return false;
355       }
356     }
357
358     return true;
359   }
360
361   private boolean letterEquals(int test, char letter, boolean ignoreCase) {
362     if (test == letter)
363       return true;
364     else if (
365       ignoreCase
366         && Character.isLowerCase(letter)
367         && test == Character.toUpperCase(letter))
368       return true;
369     else if (
370       ignoreCase
371         && Character.isUpperCase(letter)
372         && test == Character.toLowerCase(letter))
373       return true;
374
375     return false;
376   }
377
378 }