added php shortcut <?
[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.13 $
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     this.setRange(document, offset, length);
78     if (DEBUG) {
79       System.out.println(
80         "PartialRange: contentType="
81           + contentType
82           + " partitionOffset="
83           + partitionOffset);
84     }
85
86     if (this.tokens.containsKey(contentType))
87       fContentType = contentType;
88     // TODO Calculate previouse contenttype
89     if (partitionOffset > -1) {
90       partitionBorder = false;
91       fTokenOffset = partitionOffset;
92     }
93   }
94
95   /* (non-Javadoc)
96    * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
97    */
98   public int getTokenLength() {
99     return fLength;
100   }
101
102   /* (non-Javadoc)
103    * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
104    */
105   public int getTokenOffset() {
106     return fTokenOffset;
107   }
108
109   /* (non-Javadoc)
110    * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
111    */
112   public IToken nextToken() {
113     int c;
114
115     // check if we are not allready at the end of the
116     // file
117     if ((c = read()) == ICharacterScanner.EOF) {
118       partitionBorder = false;
119       return Token.EOF;
120     } else
121       unread();
122
123     if (partitionBorder) {
124       fTokenOffset = fOffset;
125       partitionBorder = false;
126     }
127
128     while ((c = read()) != ICharacterScanner.EOF) {
129       switch (c) {
130         case '<' :
131           if (fContentType
132             != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT
133             && checkPattern(new char[] { '?', 'p', 'h', 'p' }, true)) {
134             if (fContentType != IPHPPartitionScannerConstants.PHP
135               && fOffset - 5 > 0) {
136               fOffset -= 5;
137               IToken token = getToken(fContentType);
138               // save previouse contenttype
139               fPrevContentType = fContentType;
140
141               fContentType = IPHPPartitionScannerConstants.PHP;
142
143               return token;
144             } else
145               fContentType = IPHPPartitionScannerConstants.PHP;
146
147             // remember offset of this partition
148             fTokenOffset = fOffset - 5;
149           } else if (fContentType != IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT
150               && (checkPattern(new char[] { '?', ' ' }, false)
151                 || checkPattern(new char[] { '?', '\r' }, false)
152                 || checkPattern(new char[] { '?', '\n' }, false))) {
153             if (fContentType != IPHPPartitionScannerConstants.PHP
154               && fOffset - 3 > 0) {
155               fOffset -= 3;
156               IToken token = getToken(fContentType);
157               // save previouse contenttype
158               fPrevContentType = fContentType;
159
160               fContentType = IPHPPartitionScannerConstants.PHP;
161
162               return token;
163             } else
164               fContentType = IPHPPartitionScannerConstants.PHP;
165
166             // remember offset of this partition
167             fTokenOffset = fOffset - 3;
168           } else if (checkPattern(new char[] { '!', '-', '-' })) {
169             // return previouse partition
170             if (fContentType
171               != IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT
172               && fOffset - 4 > 0) {
173               fOffset -= 4;
174               IToken token = getToken(fContentType);
175
176               fContentType =
177                 IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT;
178               return token;
179             } else
180               fContentType =
181                 IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT;
182
183             fTokenOffset = fOffset - 4;
184           }
185           break;
186         case '?' :
187           if (fContentType == IPHPPartitionScannerConstants.PHP) {
188             if ((c = read()) == '>') {
189               // TODO Actually calculate the previouse contenttype from the document
190               if (fPrevContentType != null)
191                 fContentType = fPrevContentType;
192               else
193                 fContentType = IPHPPartitionScannerConstants.HTML;
194               partitionBorder = true;
195               return getToken(IPHPPartitionScannerConstants.PHP);
196             } else if (c != ICharacterScanner.EOF)
197               unread();
198           }
199           break;
200         case '-' :
201           if (fContentType
202             == IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT
203             && checkPattern(new char[] { '-', '>' })) {
204             fContentType = IPHPPartitionScannerConstants.HTML;
205             partitionBorder = true;
206             return getToken(
207               IPHPPartitionScannerConstants.HTML_MULTILINE_COMMENT);
208           }
209           break;
210         case '/' :
211           if ((c = read()) == '*') { // MULTINE COMMENT JAVASCRIPT, CSS, PHP
212             if (fContentType == IPHPPartitionScannerConstants.PHP
213               && fOffset - 2 > 0) {
214               fOffset -= 2;
215               IToken token = getToken(fContentType);
216
217               fContentType =
218                 IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT;
219
220               return token;
221             } else if (
222               fContentType
223                 == IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) {
224
225               fTokenOffset = fOffset - 2;
226             }
227
228           } else if (c != ICharacterScanner.EOF)
229             unread();
230           break;
231         case '*' :
232           if ((c = read()) == '/') {
233             if (fContentType
234               == IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT) {
235               fContentType = IPHPPartitionScannerConstants.PHP;
236               partitionBorder = true;
237
238               return getToken(
239                 IPHPPartitionScannerConstants.PHP_MULTILINE_COMMENT);
240             } else if (
241               fContentType
242                 == IPHPPartitionScannerConstants.CSS_MULTILINE_COMMENT) {
243             } else if (
244               fContentType
245                 == IPHPPartitionScannerConstants.JS_MULTILINE_COMMENT) {
246             }
247           } else if (c != ICharacterScanner.EOF)
248             unread();
249           break;
250       }
251     }
252
253     // end of file reached but we have to return the
254     // last partition.
255     return getToken(fContentType);
256   }
257   /* (non-Javadoc)
258    * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(org.eclipse.jface.text.IDocument, int, int)
259    */
260   public void setRange(IDocument document, int offset, int length) {
261     if (DEBUG) {
262       System.out.println("SET RANGE: offset=" + offset + " length=" + length);
263     }
264
265     fDocument = document;
266     fOffset = offset;
267     fTokenOffset = offset;
268     fLength = 0;
269     fEnd = fOffset + length;
270     //partitionBorder = false;
271   }
272
273   private int read() {
274     try {
275       if (fOffset < fEnd) {
276         return fDocument.getChar(fOffset++);
277       }
278       return ICharacterScanner.EOF;
279     } catch (BadLocationException e) {
280       // should never happen
281       // TODO write stacktrace to log
282       fOffset = fEnd;
283       return ICharacterScanner.EOF;
284     }
285   }
286
287   private void unread() {
288     --fOffset;
289   }
290
291   private boolean checkPattern(char[] pattern) {
292     return checkPattern(pattern, false);
293   }
294
295   /**
296    * Check if next character sequence read from document is equals to 
297    * the provided pattern. Pattern is read from left to right until the 
298    * first character read doesn't match. If this happens all read characters are
299    * unread.
300    * @param pattern The pattern to check.
301    * @return <code>true</code> if pattern is equals else returns <code>false</code>.
302    */
303   private boolean checkPattern(char[] pattern, boolean ignoreCase) {
304     int prevOffset = fOffset;
305     for (int i = 0; i < pattern.length; i++) {
306       int c = read();
307
308       if (c == ICharacterScanner.EOF
309         || !letterEquals(c, pattern[i], ignoreCase)) {
310         fOffset = prevOffset;
311         return false;
312       }
313     }
314
315     return true;
316   }
317
318   private boolean letterEquals(int test, char letter, boolean ignoreCase) {
319     if (test == letter)
320       return true;
321     else if (
322       ignoreCase
323         && Character.isLowerCase(letter)
324         && test == Character.toUpperCase(letter))
325       return true;
326     else if (
327       ignoreCase
328         && Character.isUpperCase(letter)
329         && test == Character.toLowerCase(letter))
330       return true;
331
332     return false;
333   }
334
335 }