intial source from ttp://www.sf.net/projects/wdte
[phpeclipse.git] / archive / net.sourceforge.phpeclipse.css.core / src / net / sourceforge / phpeclipse / css / core / internal / text / CssCodeReader.java
1 /*
2  * Copyright (c) 2003-2004 Christopher Lenz 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://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     Christopher Lenz - initial API and implementation
10  * 
11  * $Id: CssCodeReader.java,v 1.1 2004-09-02 18:07:13 jsurfer Exp $
12  */
13
14 package net.sourceforge.phpeclipse.css.core.internal.text;
15
16 import java.io.IOException;
17 import java.io.Reader;
18
19 import org.eclipse.jface.text.BadLocationException;
20 import org.eclipse.jface.text.IDocument;
21
22 /**
23  * 
24  */
25 public class CssCodeReader extends Reader {
26
27         // Constants ---------------------------------------------------------------
28
29         /**
30          * Constant for configuring a reader to read the document backwards.
31          */
32         public static final int BACKWARD = -1;
33         
34         /**
35          * Constant for configuring a reader to read the document forwards.
36          */
37         public static final int FORWARD = 1;
38
39         // Instance Variables ------------------------------------------------------
40
41         /**
42          * The document to read. Is <code>null</code> when the reader is closed.
43          */
44         private IDocument document;
45
46         /**
47          * The direction in which the document should be read. Can be either
48          * {@link CssCodeReader#BACKWARD} or {@link CssCodeReader#FORWARD}. 
49          */
50         private int direction;
51
52         /**
53          * The offset at which to start reading.
54          */
55         private int offset;
56         
57         /**
58          * The offset at which to end reading.
59          */
60         private int end = -1;
61         
62         /**
63          * Whether comments should be skipped.
64          */
65         private boolean skipComments;
66         
67         /**
68          * Whether string literals should be skipped.
69          */
70         private boolean skipStrings;
71         
72         /**
73          * Whether whitespace characters should be skipped.
74          */
75         private boolean skipWhitespace;
76         
77         // Constructors ------------------------------------------------------------
78
79         /**
80          * Constructor.
81          * 
82          * @param document the document to read
83          * @param offset the offset at which to start reading
84          */
85         public CssCodeReader(IDocument document, int offset) {
86                 this(document, offset, document.getLength() - offset, FORWARD);
87         }
88
89         /**
90          * Constructor.
91          * 
92          * @param document the document to read
93          * @param offset the offset at which to start reading
94          * @param length the number of characters to read at most
95          */
96         public CssCodeReader(IDocument document, int offset, int length) {
97                 this(document, offset, length, FORWARD);
98         }
99
100         /**
101          * Constructor.
102          * 
103          * @param document the document to read
104          * @param offset the offset at which to start reading
105          * @param length the number of characters to read at most
106          * @param direction the reading direction (either
107          *        {@link CssCodeReader#BACKWARD} or {@link CssCodeReader#FORWARD})
108          */
109         public CssCodeReader(IDocument document, int offset, int length,
110                 int direction) {
111                 this(document, offset, length, direction, true, false, false);
112         }
113
114         /**
115          * Constructor.
116          * 
117          * @param document the document to read
118          * @param offset the offset at which to start reading
119          * @param length the number of characters to read at most
120          * @param direction the reading direction (either
121          *        {@link CssCodeReader#BACKWARD} or {@link CssCodeReader#FORWARD})
122          * @param skipComments whether the reader should skip comments
123          * @param skipStrings whether the reader should skip strings
124          * @param skipWhitespace whether the reader should skip whitespace
125          */
126         public CssCodeReader(IDocument document, int offset, int length,
127                 int direction, boolean skipComments, boolean skipStrings,
128                 boolean skipWhitespace) {
129                 if ((direction != BACKWARD) && (direction != FORWARD)) {
130                         throw new IllegalArgumentException();
131                 }
132                 if ((offset < 0) || (offset > document.getLength())) {
133                         throw new IllegalArgumentException();
134                 }
135                 this.document = document;
136                 this.offset = offset;
137                 this.direction = direction;
138                 if (direction == FORWARD) {
139                         end = Math.min(document.getLength(), offset + length);
140                 } else {
141                         end = Math.max(0, offset - length);
142                 }
143                 this.skipComments = skipComments;
144                 this.skipStrings = skipStrings;
145                 this.skipWhitespace = skipWhitespace;
146         }
147
148         // Reader Implementation ---------------------------------------------------
149
150         /**
151          * @see Reader#close()
152          */
153         public void close() {
154                 document = null;
155         }
156         
157         /**
158          * @see Reader#read(char[], int, int)
159          */
160         public int read(char[] cbuf, int off, int len) throws IOException {
161                 for (int i = off; i < off + len; i++) {
162                         int c = read();
163                         if (c == -1) {
164                                 if (i == off) {
165                                         return -1;
166                                 } else {
167                                         return i - off;
168                                 }
169                         }
170                         cbuf[i] = (char) c;
171                 }
172                 return len;
173         }
174
175         /**
176          * @see Reader#read()
177          */
178         public int read() throws IOException {
179                 if (document == null) {
180                         throw new IllegalStateException();
181                 }
182                 try {
183                         if (direction == FORWARD) {
184                                 return readForwards();
185                         } else {
186                                 return readBackwards();
187                         }
188                 } catch (BadLocationException ble) {
189                         throw new IOException(ble.getMessage());
190                 }
191         }
192
193         // Public Methods ----------------------------------------------------------
194
195         /**
196          * Returns the document that is being read.
197          * 
198          * @return The document
199          */
200         public IDocument getDocument() {
201                 return document;
202         }
203
204         /**
205          * Returns the offset of the last read character. Should only be called 
206          * after read has been called.
207          * 
208          * @return The offset of the last read character
209          */
210         public int getOffset() {
211                 return (direction == FORWARD) ? offset - 1 : offset;
212         }
213         
214         /**
215          * Returns whether the reader is currently configured to skip comments.
216          * 
217          * @return <code>true</code> if the reader is skipping comments and
218          *         <code>false</code> otherwise
219          */
220         public boolean isSkippingComments() {
221                 return skipComments;
222         }
223
224         /**
225          * Configures the reader to skip comment blocks.
226          * 
227          * @param skipComments Whether comments should be skipped
228          */
229         public void setSkipComments(boolean skipComments) {
230                 this.skipComments = skipComments;
231         }
232
233         /**
234          * Returns whether the reader is currently configured to skip string
235          * literals.
236          * 
237          * @return <code>true</code> if the reader is skipping strings and
238          *         <code>false</code> otherwise
239          */
240         public boolean isSkippingStrings() {
241                 return skipStrings;
242         }
243
244         /**
245          * Configures the reader to skip strings.
246          * 
247          * @param skipStrings Whether strings should be skipped
248          */
249         public void setSkipStrings(boolean skipStrings) {
250                 this.skipStrings = skipStrings;
251         }
252
253         /**
254          * Returns whether the reader is currently configured to skip whitespace.
255          * 
256          * @return <code>true</code> if the reader is skipping whitespace and
257          *         <code>false</code> otherwise
258          */
259         public boolean isSkippingWhitespace() {
260                 return skipWhitespace;
261         }
262
263         /**
264          * Configures the reader to skip whitespace.
265          * 
266          * @param skipWhitespace Whether whitespace should be skipped
267          */
268         public void setSkipWhitespace(boolean skipWhitespace) {
269                 this.skipWhitespace = skipWhitespace;
270         }
271
272         // Private Methods ---------------------------------------------------------
273
274         private int readBackwards() throws BadLocationException {
275                 while (offset > 0) {
276                         offset--;
277                         char current = document.getChar(offset);
278                         switch (current) {
279                                 case '/': {
280                                         if (skipComments && (offset > 1)) {
281                                                 char next = document.getChar(offset - 1);
282                                                 if (next == '*') {
283                                                         // a comment ends, advance to the comment start
284                                                         offset -= 2;
285                                                         skipUntilStartOfComment();
286                                                         continue;
287                                                 }
288                                         }
289                                         break;
290                                 }
291                                 case '"':
292                                 case '\'': {
293                                         if (skipStrings) {
294                                                 offset--;
295                                                 skipUntilStartOfString(current);
296                                                 continue;
297                                         }
298                                         break;
299                                 }
300                                 default: {
301                                         if (skipWhitespace && Character.isWhitespace(current)) {
302                                                 continue;
303                                         }
304                                 }
305                         }
306                         return current;
307                 }
308                 return -1;
309         }
310
311         private int readForwards() throws BadLocationException {
312                 while (offset < end) {
313                         char current = document.getChar(offset++);
314                         switch (current) {
315                                 case '/': {
316                                         if (skipComments && (offset < end)) {
317                                                 char next = document.getChar(offset);
318                                                 if (next == '*') {
319                                                         // a comment starts, advance to the comment end
320                                                         offset++;
321                                                         skipUntilEndOfComment();
322                                                         continue;
323                                                 }
324                                         }
325                                         break;
326                                 }
327                                 case '"':
328                                 case '\'': {
329                                         if (skipStrings) {
330                                                 skipUntilEndOfString(current);
331                                                 continue;
332                                         }
333                                         break;
334                                 }
335                                 default: {
336                                         if (skipWhitespace && Character.isWhitespace(current)) {
337                                                 offset++;
338                                                 continue;
339                                         }
340                                 }
341                         }
342                         return current;
343                 }
344                 return -1;
345         }
346
347         private void skipUntilStartOfComment() throws BadLocationException {
348                 while (offset > 0) {
349                         char current = document.getChar(offset--);
350                         if ((current == '*') && (0 <= offset)
351                          && (document.getChar(offset) == '/')) {
352                                 return;
353                         }
354                 }
355         }
356
357         private void skipUntilEndOfComment() throws BadLocationException {
358                 while (offset < end) {
359                         char current = document.getChar(offset++);
360                         if (current == '*') {
361                                 if ((offset < end)
362                                  && (document.getChar(offset) == '/')) {
363                                         offset++;
364                                         return;
365                                 }
366                         }
367                 }
368         }
369
370         private void skipUntilStartOfString(char delimiter)
371                 throws BadLocationException {
372                 while (offset > 0) {
373                         char current = document.getChar(offset);
374                         if (current == delimiter) {
375                                 if (!((0 <= offset) && document.getChar(offset - 1) == '\\')) {
376                                         return;
377                                 }
378                         }
379                         offset--;
380                 }
381         }
382
383         private void skipUntilEndOfString(char delimiter)
384                 throws BadLocationException {
385                 while (offset < end) {
386                         char current = document.getChar(offset++);
387                         if (current == '\\') {
388                                 // ignore escaped characters
389                                 offset++;
390                         } else if (current == delimiter) {
391                                 return;
392                         }
393                 }
394         }
395
396 }