Adding two small options for code formatter (zhil's patch). These features are being...
[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.35 2007-03-17 14:07:31 axelcl 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                         // #1493165 start
318                         while (ch == ' ') {
319                                 if (position >= end) {
320                                         return false;
321                                 }
322                                 ch = document.getChar(position++);
323                         }
324                         // #1493165 end
325                         if (!Scanner.isPHPIdentifierStart(ch)) {
326                                 return false;
327                         }
328                         while (Scanner.isPHPIdentifierPart(ch)) {
329                                 buf.append(ch);
330                                 if (position >= end) {
331                                         return false;
332                                 }
333                                 ch = document.getChar(position++);
334                         }
335                         heredocIdent = buf.toString().toCharArray();
336                         while (true) {
337                                 if (position >= end) {
338                                         return false;
339                                 }
340                                 ch = document.getChar(position++);
341                                 if (ch == '\n') { // heredoc could end after a newline
342                                         int pos = 0;
343                                         while (true) {
344                                                 if (position >= end) {
345                                                         return false;
346                                                 }
347                                                 if (pos == heredocIdent.length) {
348                                                         return true;
349                                                 }
350                                                 ch = document.getChar(position++); // ignore escaped
351                                                 // character
352                                                 if (ch != heredocIdent[pos]) {
353                                                         break;
354                                                 }
355                                                 pos++;
356                                         }
357                                 }
358                         }
359                 } catch (BadLocationException e) {
360                         --position;
361                 }
362                 return false;
363         }
364
365         private boolean readSingleLine() {
366                 try {
367                         do {
368                                 if (position >= end) {
369                                         return false;
370                                 }
371                         } while (document.getChar(position++) != '\n');
372                         return true;
373                 } catch (BadLocationException e) {
374                         --position;
375                 }
376                 return false;
377         }
378
379         private boolean readMultiLineComment() {
380                 try {
381                         char ch;
382                         while (true) {
383                                 if (position >= end) {
384                                         return false;
385                                 }
386                                 ch = document.getChar(position++);
387                                 if (ch == '*') {
388                                         if (position >= end) {
389                                                 return false;
390                                         }
391                                         if (document.getChar(position) == '/') {
392                                                 position++;
393                                                 return true;
394                                         }
395                                 }
396                         }
397                 } catch (BadLocationException e) {
398                         --position;
399                 }
400                 return false;
401         }
402
403         private void unread() {
404                 --position;
405         }
406
407         /*
408          * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
409          */
410         public int getTokenOffset() {
411                 if (AbstractPartitioner.DEBUG) {
412                         Assert.isTrue(offset >= 0, Integer.toString(offset));
413                 }
414                 return offset;
415         }
416
417         /*
418          * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
419          */
420         public int getTokenLength() {
421                 return length;
422         }
423
424         /*
425          * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(IDocument, int,
426          *      int)
427          */
428         public void setRange(IDocument document, int offset, int length) {
429                 this.document = document;
430                 // this.begin = offset;
431                 this.end = offset + length;
432
433                 this.offset = offset;
434                 this.position = offset;
435                 this.length = 0;
436         }
437
438         /*
439          * @see org.eclipse.jface.text.rules.IPartitionTokenScanner
440          */
441         public void setPartialRange(IDocument document, int offset, int length,
442                         String contentType, int partitionOffset) {
443                 // state = STATE_DEFAULT;
444                 if (partitionOffset > -1) {
445                         int delta = offset - partitionOffset;
446                         if (delta > 0) {
447                                 setRange(document, partitionOffset, length + delta);
448                                 return;
449                         }
450                 }
451                 setRange(document, partitionOffset, length);
452         }
453
454         // private boolean isContinuationPartition(IDocument document, int offset) {
455         // try {
456         // String type = document.getContentType(offset - 1);
457         //
458         // if (type != IDocument.DEFAULT_CONTENT_TYPE) {
459         // return true;
460         // }
461         // } catch (BadLocationException e) {}
462         //
463         // return false;
464         // }
465 }