Bumped up versions to 1.2.0
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPPartitionScanner.java
1 /**********************************************************************
2  Copyright (c) 2002  Widespace, OU  and others.
3  All rights reserved.   This program and the accompanying materials
4  are made available under the terms of the Common Public License v1.0
5  which accompanies this distribution, and is available at
6  http://solareclipse.sourceforge.net/legal/cpl-v10.html
7
8  Contributors:
9  Igor Malinin - initial contribution
10
11  $Id: PHPPartitionScanner.java,v 1.34 2006-10-21 23:18:32 pombredanne Exp $
12  **********************************************************************/
13 package net.sourceforge.phpeclipse.phpeditor.php;
14
15 import java.util.HashMap;
16 import java.util.Map;
17
18 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
19 import net.sourceforge.phpeclipse.ui.text.rules.AbstractPartitioner;
20
21 import org.eclipse.jface.text.Assert;
22 import org.eclipse.jface.text.BadLocationException;
23 import org.eclipse.jface.text.IDocument;
24 import org.eclipse.jface.text.rules.ICharacterScanner;
25 import org.eclipse.jface.text.rules.IPartitionTokenScanner;
26 import org.eclipse.jface.text.rules.IToken;
27 import org.eclipse.jface.text.rules.Token;
28
29 /**
30  * 
31  * 
32  * @author Igor Malinin
33  */
34 public class PHPPartitionScanner implements IPartitionTokenScanner {
35         public static final String PHP_SCRIPTING_AREA = "__php_scripting_area ";
36
37         public static final int STATE_DEFAULT = 0;
38
39         // public static final int STATE_TAG = 1;
40         // public static final int STATE_SCRIPT = 2;
41
42         private IDocument document;
43
44         // private int begin;
45
46         private int end;
47
48         private int offset;
49
50         private int length;
51
52         private int position;
53
54         // private int state;
55
56         private Map tokens = new HashMap();
57
58         public PHPPartitionScanner() {
59         }
60
61         /*
62          * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
63          */
64         public IToken nextToken() {
65                 offset += length;
66
67                 /*
68                  * switch (state) { case STATE_TAG: return nextTagToken(); }
69                  */
70
71                 switch (read()) {
72                 case ICharacterScanner.EOF:
73                         // state = STATE_DEFAULT;
74                         return getToken(null);
75
76                 case '<':
77                         switch (read()) {
78                         case ICharacterScanner.EOF:
79                                 // state = STATE_DEFAULT;
80                                 return getToken(null);
81
82                         case '?': // <?
83                                 // int ch = read();
84                                 //
85                                 // switch (ch) {
86                                 // case ICharacterScanner.EOF:
87                                 // state = STATE_DEFAULT;
88                                 // return getToken(PHP_SCRIPTING_AREA);
89                                 // }
90                                 return scanUntilPHPEndToken(PHP_SCRIPTING_AREA);
91                         }
92
93                         unread();
94                 }
95
96                 loop: while (true) {
97                         switch (read()) {
98                         case ICharacterScanner.EOF:
99                                 // state = STATE_DEFAULT;
100                                 return getToken(null);
101
102                         case '<':
103                                 switch (read()) {
104                                 case ICharacterScanner.EOF:
105                                         // state = STATE_DEFAULT;
106                                         return getToken(null);
107
108                                 case '?':
109                                         unread();
110                                         break;
111
112                                 case '<':
113                                         unread();
114
115                                 default:
116                                         continue loop;
117                                 }
118
119                                 unread();
120
121                                 // state = STATE_DEFAULT;
122                                 return getToken(null);
123                         }
124                 }
125         }
126
127         private IToken scanUntilPHPEndToken(String token) {
128                 int ch = read();
129                 while (true) {
130                         switch (ch) {
131                         case ICharacterScanner.EOF:
132                                 // state = STATE_DEFAULT;
133                                 return getToken(token);
134                         case '"': // double quoted string
135                                 // read until end of double quoted string
136                                 if (!readUntilEscapedDQ()) {
137                                         // state = STATE_DEFAULT;
138                                         return getToken(token);
139                                 }
140                                 break;
141                         case '<': // heredoc string
142                                 ch = read();
143                                 switch (ch) {
144                                 case ICharacterScanner.EOF:
145                                         break;
146                                 case '<':
147                                         ch = read();
148                                         switch (ch) {
149                                         case ICharacterScanner.EOF:
150                                                 break;
151                                         case '<':
152                                                 // read until end of heredoc string
153                                                 if (!readUntilEscapedHEREDOC()) {
154                                                         // state = STATE_DEFAULT;
155                                                         return getToken(token);
156                                                 }
157                                         }
158                                 }
159                                 break;
160                         case '\'': // single quoted string
161                                 // read until end of single quoted string
162                                 if (!readUntilEscapedSQ()) {
163                                         // state = STATE_DEFAULT;
164                                         return getToken(token);
165                                 }
166                                 break;
167                         case '/': // comment start?
168                                 ch = read();
169                                 switch (ch) {
170                                 case ICharacterScanner.EOF:
171                                         break;
172                                 case '/':
173                                         // read until end of line
174                                         if (!readSingleLine()) {
175                                                 // state = STATE_DEFAULT;
176                                                 return getToken(token);
177                                         }
178                                         break;
179                                 case '*':
180                                         // read until end of comment
181                                         if (!readMultiLineComment()) {
182                                                 // state = STATE_DEFAULT;
183                                                 return getToken(token);
184                                         }
185                                         break;
186                                 default:
187                                         continue;
188                                 }
189                                 break;
190                         case '#': // line comment
191                                 // read until end of line
192                                 if (!readSingleLine()) {
193                                         // state = STATE_DEFAULT;
194                                         return getToken(token);
195                                 }
196                                 break;
197                         case '?':
198                                 ch = read();
199                                 switch (ch) {
200                                 case ICharacterScanner.EOF:
201                                 case '>':
202                                         // state = STATE_DEFAULT;
203                                         return getToken(token);
204
205                                 case '?':
206                                         continue;
207                                 default:
208                                         continue;
209                                 }
210                         }
211
212                         ch = read();
213                 }
214         }
215
216         private IToken getToken(String type) {
217                 length = position - offset;
218
219                 if (length == 0) {
220                         return Token.EOF;
221                 }
222
223                 // if (length<0) {
224                 // try {
225                 // System.out.println("Length<0:"+document.get(offset,5)+""+length);
226                 // } catch (BadLocationException e) {
227                 // e.printStackTrace();
228                 // }
229                 // }
230
231                 if (type == null) {
232                         return Token.UNDEFINED;
233                 }
234
235                 IToken token = (IToken) tokens.get(type);
236                 if (token == null) {
237                         token = new Token(type);
238                         tokens.put(type, token);
239                 }
240
241                 return token;
242         }
243
244         private int read() {
245                 if (position >= end) {
246                         return ICharacterScanner.EOF;
247                 }
248
249                 try {
250                         return document.getChar(position++);
251                 } catch (BadLocationException e) {
252                         --position;
253                         return ICharacterScanner.EOF;
254                 }
255         }
256
257         private boolean readUntilEscapedDQ() {
258                 // search last double quoted character
259                 try {
260                         char ch;
261                         while (true) {
262                                 if (position >= end) {
263                                         return false;
264                                 }
265                                 ch = document.getChar(position++);
266                                 if (ch == '\\') {
267                                         if (position >= end) {
268                                                 return false;
269                                         }
270                                         ch = document.getChar(position++); // ignore escaped
271                                                                                                                 // character
272                                 } else if (ch == '"') {
273                                         return true;
274                                 }
275                         }
276                 } catch (BadLocationException e) {
277                         --position;
278                 }
279                 return false;
280         }
281
282         private boolean readUntilEscapedSQ() {
283                 // search last single quoted character
284                 try {
285                         char ch;
286                         while (true) {
287                                 if (position >= end) {
288                                         return false;
289                                 }
290                                 ch = document.getChar(position++);
291                                 if (ch == '\\') {
292                                         if (position >= end) {
293                                                 return false;
294                                         }
295                                         ch = document.getChar(position++); // ignore escaped
296                                                                                                                 // character
297                                 } else if (ch == '\'') {
298                                         return true;
299                                 }
300                         }
301                 } catch (BadLocationException e) {
302                         --position;
303                 }
304                 return false;
305         }
306
307         private boolean readUntilEscapedHEREDOC() {
308                 // search until heredoc ends
309                 try {
310                         char ch;
311                         StringBuffer buf = new StringBuffer();
312                         char[] heredocIdent;
313                         if (position >= end) {
314                                 return false;
315                         }
316                         ch = document.getChar(position++);
317                         if (!Scanner.isPHPIdentifierStart(ch)) {
318                                 return false;
319                         }
320                         while (Scanner.isPHPIdentifierPart(ch)) {
321                                 buf.append(ch);
322                                 if (position >= end) {
323                                         return false;
324                                 }
325                                 ch = document.getChar(position++);
326                         }
327                         heredocIdent = buf.toString().toCharArray();
328                         while (true) {
329                                 if (position >= end) {
330                                         return false;
331                                 }
332                                 ch = document.getChar(position++);
333                                 if (ch == '\n') { // heredoc could end after a newline
334                                         int pos = 0;
335                                         while (true) {
336                                                 if (position >= end) {
337                                                         return false;
338                                                 }
339                                                 if (pos == heredocIdent.length) {
340                                                         return true;
341                                                 }
342                                                 ch = document.getChar(position++); // ignore escaped
343                                                                                                                         // character
344                                                 if (ch != heredocIdent[pos]) {
345                                                         break;
346                                                 }
347                                                 pos++;
348                                         }
349                                 }
350                         }
351                 } catch (BadLocationException e) {
352                         --position;
353                 }
354                 return false;
355         }
356
357         private boolean readSingleLine() {
358                 try {
359                         do {
360                                 if (position >= end) {
361                                         return false;
362                                 }
363                         } while (document.getChar(position++) != '\n');
364                         return true;
365                 } catch (BadLocationException e) {
366                         --position;
367                 }
368                 return false;
369         }
370
371         private boolean readMultiLineComment() {
372                 try {
373                         char ch;
374                         while (true) {
375                                 if (position >= end) {
376                                         return false;
377                                 }
378                                 ch = document.getChar(position++);
379                                 if (ch == '*') {
380                                         if (position >= end) {
381                                                 return false;
382                                         }
383                                         if (document.getChar(position) == '/') {
384                                                 position++;
385                                                 return true;
386                                         }
387                                 }
388                         }
389                 } catch (BadLocationException e) {
390                         --position;
391                 }
392                 return false;
393         }
394
395         private void unread() {
396                 --position;
397         }
398
399         /*
400          * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
401          */
402         public int getTokenOffset() {
403                 if (AbstractPartitioner.DEBUG) {
404                         Assert.isTrue(offset >= 0, Integer.toString(offset));
405                 }
406                 return offset;
407         }
408
409         /*
410          * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
411          */
412         public int getTokenLength() {
413                 return length;
414         }
415
416         /*
417          * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(IDocument, int,
418          *      int)
419          */
420         public void setRange(IDocument document, int offset, int length) {
421                 this.document = document;
422                 // this.begin = offset;
423                 this.end = offset + length;
424
425                 this.offset = offset;
426                 this.position = offset;
427                 this.length = 0;
428         }
429
430         /*
431          * @see org.eclipse.jface.text.rules.IPartitionTokenScanner
432          */
433         public void setPartialRange(IDocument document, int offset, int length,
434                         String contentType, int partitionOffset) {
435                 // state = STATE_DEFAULT;
436                 if (partitionOffset > -1) {
437                         int delta = offset - partitionOffset;
438                         if (delta > 0) {
439                                 setRange(document, partitionOffset, length + delta);
440                                 return;
441                         }
442                 }
443                 setRange(document, partitionOffset, length);
444         }
445
446         // private boolean isContinuationPartition(IDocument document, int offset) {
447         // try {
448         // String type = document.getContentType(offset - 1);
449         //
450         // if (type != IDocument.DEFAULT_CONTENT_TYPE) {
451         // return true;
452         // }
453         // } catch (BadLocationException e) {}
454         //
455         // return false;
456         // }
457 }