Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.swing / org.gvsig.scripting.swing.impl / src / main / java / org / gvsig / scripting / swing / impl / syntaxhighlight / styles / JavaStyledDocument.java @ 1440

History | View | Annotate | Download (13.8 KB)

1 165 jobacas
package org.gvsig.scripting.swing.impl.syntaxhighlight.styles;
2
3
import java.awt.Color;
4
import java.util.HashSet;
5 212 cordinyana
import java.util.Set;
6 165 jobacas
7
import javax.swing.text.AttributeSet;
8
import javax.swing.text.BadLocationException;
9
import javax.swing.text.DefaultEditorKit;
10
import javax.swing.text.DefaultStyledDocument;
11
import javax.swing.text.Element;
12
import javax.swing.text.MutableAttributeSet;
13
import javax.swing.text.SimpleAttributeSet;
14
import javax.swing.text.StyleConstants;
15
16
17
public class JavaStyledDocument extends DefaultStyledDocument {
18
        /**
19
         *
20
         */
21
        private static final long serialVersionUID = 2859530660266127826L;
22
23 212 cordinyana
        private final DefaultStyledDocument doc;
24
        private final Element rootElement;
25 165 jobacas
26
        private boolean multiLineComment;
27 212 cordinyana
        private final MutableAttributeSet normal;
28
        private final MutableAttributeSet keyword;
29
        private final MutableAttributeSet comment;
30
        private final MutableAttributeSet quote;
31 165 jobacas
32 212 cordinyana
    private final Set<String> keywords;
33 165 jobacas
34
        public JavaStyledDocument()
35
        {
36
                doc = this;
37
                rootElement = doc.getDefaultRootElement();
38
                putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );
39
40
                normal = new SimpleAttributeSet();
41
                StyleConstants.setForeground(normal, Color.black);
42
43
                comment = new SimpleAttributeSet();
44
                StyleConstants.setForeground(comment, Color.gray);
45
                StyleConstants.setItalic(comment, true);
46
47
                keyword = new SimpleAttributeSet();
48
                StyleConstants.setForeground(keyword, Color.blue);
49
50
                quote = new SimpleAttributeSet();
51
                StyleConstants.setForeground(quote, Color.red);
52
53 212 cordinyana
        keywords = new HashSet<String>();
54 165 jobacas
                keywords.add( "abstract" );
55
                keywords.add( "boolean" );
56
                keywords.add( "break" );
57
                keywords.add( "byte" );
58
                keywords.add( "byvalue" );
59
                keywords.add( "case" );
60
                keywords.add( "cast" );
61
                keywords.add( "catch" );
62
                keywords.add( "char" );
63
                keywords.add( "class" );
64
                keywords.add( "const" );
65
                keywords.add( "continue" );
66
                keywords.add( "default" );
67
                keywords.add( "do" );
68
                keywords.add( "double" );
69
                keywords.add( "else" );
70
                keywords.add( "extends" );
71
                keywords.add( "false" );
72
                keywords.add( "final" );
73
                keywords.add( "finally" );
74
                keywords.add( "float" );
75
                keywords.add( "for" );
76
                keywords.add( "future" );
77
                keywords.add( "generic" );
78
                keywords.add( "goto" );
79
                keywords.add( "if" );
80
                keywords.add( "implements" );
81
                keywords.add( "import" );
82
                keywords.add( "inner" );
83
                keywords.add( "instanceof" );
84
                keywords.add( "int" );
85
                keywords.add( "interface" );
86
                keywords.add( "long" );
87
                keywords.add( "native" );
88
                keywords.add( "new" );
89
                keywords.add( "null" );
90
                keywords.add( "operator" );
91
                keywords.add( "outer" );
92
                keywords.add( "package" );
93
                keywords.add( "private" );
94
                keywords.add( "protected" );
95
                keywords.add( "public" );
96
                keywords.add( "rest" );
97
                keywords.add( "return" );
98
                keywords.add( "short" );
99
                keywords.add( "static" );
100
                keywords.add( "super" );
101
                keywords.add( "switch" );
102
                keywords.add( "synchronized" );
103
                keywords.add( "this" );
104
                keywords.add( "throw" );
105
                keywords.add( "throws" );
106
                keywords.add( "transient" );
107
                keywords.add( "true" );
108
                keywords.add( "try" );
109
                keywords.add( "var" );
110
                keywords.add( "void" );
111
                keywords.add( "volatile" );
112
                keywords.add( "while" );
113
        }
114
115
        /*
116
         *  Override to apply syntax highlighting after the document has been updated
117
         */
118 212 cordinyana
        @Override
119
    public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
120 165 jobacas
        {
121
                if (str.equals("{"))
122
                        str = addMatchingBrace(offset);
123
124
                super.insertString(offset, str, a);
125
                processChangedLines(offset, str.length());
126
        }
127
128
        /*
129
         *  Override to apply syntax highlighting after the document has been updated
130
         */
131 212 cordinyana
        @Override
132
    public void remove(int offset, int length) throws BadLocationException
133 165 jobacas
        {
134
                super.remove(offset, length);
135
                processChangedLines(offset, 0);
136
        }
137
138
        /*
139
         *  Determine how many lines have been changed,
140
         *  then apply highlighting to each line
141
         */
142
        public void processChangedLines(int offset, int length)
143
                throws BadLocationException
144
        {
145
                String content = doc.getText(0, doc.getLength());
146
147
                //  The lines affected by the latest document update
148
149
                int startLine = rootElement.getElementIndex( offset );
150
                int endLine = rootElement.getElementIndex( offset + length );
151
152
                //  Make sure all comment lines prior to the start line are commented
153
                //  and determine if the start line is still in a multi line comment
154
155
                setMultiLineComment( commentLinesBefore( content, startLine ) );
156
157
                //  Do the actual highlighting
158
159
                for (int i = startLine; i <= endLine; i++)
160
                {
161
                        applyHighlighting(content, i);
162
                }
163
164
                //  Resolve highlighting to the next end multi line delimiter
165
166
                if (isMultiLineComment())
167
                        commentLinesAfter(content, endLine);
168
                else
169
                        highlightLinesAfter(content, endLine);
170
        }
171
172
        /*
173
         *  Highlight lines when a multi line comment is still 'open'
174
         *  (ie. matching end delimiter has not yet been encountered)
175
         */
176
        private boolean commentLinesBefore(String content, int line)
177
        {
178
                int offset = rootElement.getElement( line ).getStartOffset();
179
180
                //  Start of comment not found, nothing to do
181
182
                int startDelimiter = lastIndexOf( content, getStartDelimiter(), offset - 2 );
183
184
                if (startDelimiter < 0)
185
                        return false;
186
187
                //  Matching start/end of comment found, nothing to do
188
189
                int endDelimiter = indexOf( content, getEndDelimiter(), startDelimiter );
190
191
                if (endDelimiter < offset & endDelimiter != -1)
192
                        return false;
193
194
                //  End of comment not found, highlight the lines
195
196
                doc.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1, comment, false);
197
                return true;
198
        }
199
200
        /*
201
         *  Highlight comment lines to matching end delimiter
202
         */
203
        private void commentLinesAfter(String content, int line)
204
        {
205
                int offset = rootElement.getElement( line ).getEndOffset();
206
207
                //  End of comment not found, nothing to do
208
209
                int endDelimiter = indexOf( content, getEndDelimiter(), offset );
210
211
                if (endDelimiter < 0)
212
                        return;
213
214
                //  Matching start/end of comment found, comment the lines
215
216
                int startDelimiter = lastIndexOf( content, getStartDelimiter(), endDelimiter );
217
218
                if (startDelimiter < 0 || startDelimiter <= offset)
219
                {
220
                        doc.setCharacterAttributes(offset, endDelimiter - offset + 1, comment, false);
221
                }
222
        }
223
224
        /*
225
         *  Highlight lines to start or end delimiter
226
         */
227
        private void highlightLinesAfter(String content, int line)
228
                throws BadLocationException
229
        {
230
                int offset = rootElement.getElement( line ).getEndOffset();
231
232
                //  Start/End delimiter not found, nothing to do
233
234
                int startDelimiter = indexOf( content, getStartDelimiter(), offset );
235
                int endDelimiter = indexOf( content, getEndDelimiter(), offset );
236
237
                if (startDelimiter < 0)
238
                        startDelimiter = content.length();
239
240
                if (endDelimiter < 0)
241
                        endDelimiter = content.length();
242
243
                int delimiter = Math.min(startDelimiter, endDelimiter);
244
245
                if (delimiter < offset)
246
                        return;
247
248
                //        Start/End delimiter found, reapply highlighting
249
250
                int endLine = rootElement.getElementIndex( delimiter );
251
252
                for (int i = line + 1; i < endLine; i++)
253
                {
254
                        Element branch = rootElement.getElement( i );
255
                        Element leaf = doc.getCharacterElement( branch.getStartOffset() );
256
                        AttributeSet as = leaf.getAttributes();
257
258
                        if ( as.isEqual(comment) )
259
                                applyHighlighting(content, i);
260
                }
261
        }
262
263
        /*
264
         *  Parse the line to determine the appropriate highlighting
265
         */
266
        private void applyHighlighting(String content, int line)
267
                throws BadLocationException
268
        {
269
                int startOffset = rootElement.getElement( line ).getStartOffset();
270
                int endOffset = rootElement.getElement( line ).getEndOffset() - 1;
271
272
                int lineLength = endOffset - startOffset;
273
                int contentLength = content.length();
274
275
                if (endOffset >= contentLength)
276
                        endOffset = contentLength - 1;
277
278
                //  check for multi line comments
279
                //  (always set the comment attribute for the entire line)
280
281
                if (endingMultiLineComment(content, startOffset, endOffset)
282
                ||  isMultiLineComment()
283
                ||  startingMultiLineComment(content, startOffset, endOffset) )
284
                {
285
                        doc.setCharacterAttributes(startOffset, endOffset - startOffset + 1, comment, false);
286
                        return;
287
                }
288
289
                //  set normal attributes for the line
290
291
                doc.setCharacterAttributes(startOffset, lineLength, normal, true);
292
293
                //  check for single line comment
294
295
                int index = content.indexOf(getSingleLineDelimiter(), startOffset);
296
297
                if ( (index > -1) && (index < endOffset) )
298
                {
299
                        doc.setCharacterAttributes(index, endOffset - index + 1, comment, false);
300
                        endOffset = index - 1;
301
                }
302
303
                //  check for tokens
304
305
                checkForTokens(content, startOffset, endOffset);
306
        }
307
308
        /*
309
         *  Does this line contain the start delimiter
310
         */
311
        private boolean startingMultiLineComment(String content, int startOffset, int endOffset)
312
                throws BadLocationException
313
        {
314
                int index = indexOf( content, getStartDelimiter(), startOffset );
315
316
                if ( (index < 0) || (index > endOffset) )
317
                        return false;
318
                else
319
                {
320
                        setMultiLineComment( true );
321
                        return true;
322
                }
323
        }
324
325
        /*
326
         *  Does this line contain the end delimiter
327
         */
328
        private boolean endingMultiLineComment(String content, int startOffset, int endOffset)
329
                throws BadLocationException
330
        {
331
                int index = indexOf( content, getEndDelimiter(), startOffset );
332
333
                if ( (index < 0) || (index > endOffset) )
334
                        return false;
335
                else
336
                {
337
                        setMultiLineComment( false );
338
                        return true;
339
                }
340
        }
341
342
        /*
343
         *  We have found a start delimiter
344
         *  and are still searching for the end delimiter
345
         */
346
        private boolean isMultiLineComment()
347
        {
348
                return multiLineComment;
349
        }
350
351
        private void setMultiLineComment(boolean value)
352
        {
353
                multiLineComment = value;
354
        }
355
356
        /*
357
         *        Parse the line for tokens to highlight
358
         */
359
        private void checkForTokens(String content, int startOffset, int endOffset)
360
        {
361
                while (startOffset <= endOffset)
362
                {
363
                        //  skip the delimiters to find the start of a new token
364
365
                        while ( isDelimiter( content.substring(startOffset, startOffset + 1) ) )
366
                        {
367
                                if (startOffset < endOffset)
368
                                        startOffset++;
369
                                else
370
                                        return;
371
                        }
372
373
                        //  Extract and process the entire token
374
375
                        if ( isQuoteDelimiter( content.substring(startOffset, startOffset + 1) ) )
376
                                startOffset = getQuoteToken(content, startOffset, endOffset);
377
                        else
378
                                startOffset = getOtherToken(content, startOffset, endOffset);
379
                }
380
        }
381
382
        /*
383
         *
384
         */
385
        private int getQuoteToken(String content, int startOffset, int endOffset)
386
        {
387
                String quoteDelimiter = content.substring(startOffset, startOffset + 1);
388
                String escapeString = getEscapeString(quoteDelimiter);
389
390
                int index;
391
                int endOfQuote = startOffset;
392
393
                //  skip over the escape quotes in this quote
394
395
                index = content.indexOf(escapeString, endOfQuote + 1);
396
397
                while ( (index > -1) && (index < endOffset) )
398
                {
399
                        endOfQuote = index + 1;
400
                        index = content.indexOf(escapeString, endOfQuote);
401
                }
402
403
                // now find the matching delimiter
404
405
                index = content.indexOf(quoteDelimiter, endOfQuote + 1);
406
407
                if ( (index < 0) || (index > endOffset) )
408
                        endOfQuote = endOffset;
409
                else
410
                        endOfQuote = index;
411
412
                doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, quote, false);
413
414
                return endOfQuote + 1;
415
        }
416
417
        /*
418
         *
419
         */
420
        private int getOtherToken(String content, int startOffset, int endOffset)
421
        {
422
                int endOfToken = startOffset + 1;
423
424
                while ( endOfToken <= endOffset )
425
                {
426
                        if ( isDelimiter( content.substring(endOfToken, endOfToken + 1) ) )
427
                                break;
428
429
                        endOfToken++;
430
                }
431
432
                String token = content.substring(startOffset, endOfToken);
433
434
                if ( isKeyword( token ) )
435
                {
436
                        doc.setCharacterAttributes(startOffset, endOfToken - startOffset, keyword, false);
437
                }
438
439
                return endOfToken + 1;
440
        }
441
442
        /*
443
         *  Assume the needle will the found at the start/end of the line
444
         */
445
        private int indexOf(String content, String needle, int offset)
446
        {
447
                int index;
448
449
                while ( (index = content.indexOf(needle, offset)) != -1 )
450
                {
451
                        String text = getLine( content, index ).trim();
452
453
                        if (text.startsWith(needle) || text.endsWith(needle))
454
                                break;
455
                        else
456
                                offset = index + 1;
457
                }
458
459
                return index;
460
        }
461
462
        /*
463
         *  Assume the needle will the found at the start/end of the line
464
         */
465
        private int lastIndexOf(String content, String needle, int offset)
466
        {
467
                int index;
468
469
                while ( (index = content.lastIndexOf(needle, offset)) != -1 )
470
                {
471
                        String text = getLine( content, index ).trim();
472
473
                        if (text.startsWith(needle) || text.endsWith(needle))
474
                                break;
475
                        else
476
                                offset = index - 1;
477
                }
478
479
                return index;
480
        }
481
482
        private String getLine(String content, int offset)
483
        {
484
                int line = rootElement.getElementIndex( offset );
485
                Element lineElement = rootElement.getElement( line );
486
                int start = lineElement.getStartOffset();
487
                int end = lineElement.getEndOffset();
488
                return content.substring(start, end - 1);
489
        }
490
491
        /*
492
         *  Override for other languages
493
         */
494
        protected boolean isDelimiter(String character)
495
        {
496
                String operands = ";:{}()[]+-/%<=>!&|^~*";
497
498
                if (Character.isWhitespace( character.charAt(0) ) ||
499
                        operands.indexOf(character) != -1 )
500
                        return true;
501
                else
502
                        return false;
503
        }
504
505
        /*
506
         *  Override for other languages
507
         */
508
        protected boolean isQuoteDelimiter(String character)
509
        {
510
                String quoteDelimiters = "\"'";
511
512
                if (quoteDelimiters.indexOf(character) < 0)
513
                        return false;
514
                else
515
                        return true;
516
        }
517
518
        /*
519
         *  Override for other languages
520
         */
521
        protected boolean isKeyword(String token)
522
        {
523
                return keywords.contains( token );
524
        }
525
526
        /*
527
         *  Override for other languages
528
         */
529
        protected String getStartDelimiter()
530
        {
531
                return "/*";
532
        }
533
534
        /*
535
         *  Override for other languages
536
         */
537
        protected String getEndDelimiter()
538
        {
539
                return "*/";
540
        }
541
542
        /*
543
         *  Override for other languages
544
         */
545
        protected String getSingleLineDelimiter()
546
        {
547
                return "//";
548
        }
549
550
        /*
551
         *  Override for other languages
552
         */
553
        protected String getEscapeString(String quoteDelimiter)
554
        {
555
                return "\\" + quoteDelimiter;
556
        }
557
558
        /*
559
         *
560
         */
561
        protected String addMatchingBrace(int offset) throws BadLocationException
562
        {
563
                StringBuffer whiteSpace = new StringBuffer();
564
                int line = rootElement.getElementIndex( offset );
565
                int i = rootElement.getElement(line).getStartOffset();
566
567
                while (true)
568
                {
569
                        String temp = doc.getText(i, 1);
570
571
                        if (temp.equals(" ") || temp.equals("\t"))
572
                        {
573
                                whiteSpace.append(temp);
574
                                i++;
575
                        }
576
                        else
577
                                break;
578
                }
579
580
                return "{\n" + whiteSpace.toString() + "\t\n" + whiteSpace.toString() + "}";
581
        }
582
583
}