Statistics
| Revision:

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 @ 212

History | View | Annotate | Download (13.8 KB)

1
package org.gvsig.scripting.swing.impl.syntaxhighlight.styles;
2

    
3
import java.awt.Color;
4
import java.util.HashSet;
5
import java.util.Set;
6

    
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
        private final DefaultStyledDocument doc;
24
        private final Element rootElement;
25

    
26
        private boolean multiLineComment;
27
        private final MutableAttributeSet normal;
28
        private final MutableAttributeSet keyword;
29
        private final MutableAttributeSet comment;
30
        private final MutableAttributeSet quote;
31

    
32
    private final Set<String> keywords;
33

    
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
        keywords = new HashSet<String>();
54
                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
        @Override
119
    public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
120
        {
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
        @Override
132
    public void remove(int offset, int length) throws BadLocationException
133
        {
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
}
584