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