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

History | View | Annotate | Download (13.7 KB)

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

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

    
6
import javax.swing.text.AttributeSet;
7
import javax.swing.text.BadLocationException;
8
import javax.swing.text.DefaultEditorKit;
9
import javax.swing.text.DefaultStyledDocument;
10
import javax.swing.text.Element;
11
import javax.swing.text.MutableAttributeSet;
12
import javax.swing.text.SimpleAttributeSet;
13
import javax.swing.text.StyleConstants;
14

    
15

    
16
public class JavaStyledDocument extends DefaultStyledDocument {
17
        /**
18
         * 
19
         */
20
        private static final long serialVersionUID = 2859530660266127826L;
21
        
22
        private DefaultStyledDocument doc;
23
        private Element rootElement;
24

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

    
31
        private HashSet keywords;
32

    
33
        public JavaStyledDocument()
34
        {
35
                doc = this;
36
                rootElement = doc.getDefaultRootElement();
37
                putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );
38

    
39
                normal = new SimpleAttributeSet();
40
                StyleConstants.setForeground(normal, Color.black);
41

    
42
                comment = new SimpleAttributeSet();
43
                StyleConstants.setForeground(comment, Color.gray);
44
                StyleConstants.setItalic(comment, true);
45

    
46
                keyword = new SimpleAttributeSet();
47
                StyleConstants.setForeground(keyword, Color.blue);
48

    
49
                quote = new SimpleAttributeSet();
50
                StyleConstants.setForeground(quote, Color.red);
51

    
52
                keywords = new HashSet();
53
                keywords.add( "abstract" );
54
                keywords.add( "boolean" );
55
                keywords.add( "break" );
56
                keywords.add( "byte" );
57
                keywords.add( "byvalue" );
58
                keywords.add( "case" );
59
                keywords.add( "cast" );
60
                keywords.add( "catch" );
61
                keywords.add( "char" );
62
                keywords.add( "class" );
63
                keywords.add( "const" );
64
                keywords.add( "continue" );
65
                keywords.add( "default" );
66
                keywords.add( "do" );
67
                keywords.add( "double" );
68
                keywords.add( "else" );
69
                keywords.add( "extends" );
70
                keywords.add( "false" );
71
                keywords.add( "final" );
72
                keywords.add( "finally" );
73
                keywords.add( "float" );
74
                keywords.add( "for" );
75
                keywords.add( "future" );
76
                keywords.add( "generic" );
77
                keywords.add( "goto" );
78
                keywords.add( "if" );
79
                keywords.add( "implements" );
80
                keywords.add( "import" );
81
                keywords.add( "inner" );
82
                keywords.add( "instanceof" );
83
                keywords.add( "int" );
84
                keywords.add( "interface" );
85
                keywords.add( "long" );
86
                keywords.add( "native" );
87
                keywords.add( "new" );
88
                keywords.add( "null" );
89
                keywords.add( "operator" );
90
                keywords.add( "outer" );
91
                keywords.add( "package" );
92
                keywords.add( "private" );
93
                keywords.add( "protected" );
94
                keywords.add( "public" );
95
                keywords.add( "rest" );
96
                keywords.add( "return" );
97
                keywords.add( "short" );
98
                keywords.add( "static" );
99
                keywords.add( "super" );
100
                keywords.add( "switch" );
101
                keywords.add( "synchronized" );
102
                keywords.add( "this" );
103
                keywords.add( "throw" );
104
                keywords.add( "throws" );
105
                keywords.add( "transient" );
106
                keywords.add( "true" );
107
                keywords.add( "try" );
108
                keywords.add( "var" );
109
                keywords.add( "void" );
110
                keywords.add( "volatile" );
111
                keywords.add( "while" );
112
        }
113

    
114
        /*
115
         *  Override to apply syntax highlighting after the document has been updated
116
         */
117
        public void insertString(int offset, String str, AttributeSet a) throws BadLocationException
118
        {
119
                if (str.equals("{"))
120
                        str = addMatchingBrace(offset);
121

    
122
                super.insertString(offset, str, a);
123
                processChangedLines(offset, str.length());
124
        }
125

    
126
        /*
127
         *  Override to apply syntax highlighting after the document has been updated
128
         */
129
        public void remove(int offset, int length) throws BadLocationException
130
        {
131
                super.remove(offset, length);
132
                processChangedLines(offset, 0);
133
        }
134

    
135
        /*
136
         *  Determine how many lines have been changed,
137
         *  then apply highlighting to each line
138
         */
139
        public void processChangedLines(int offset, int length)
140
                throws BadLocationException
141
        {
142
                String content = doc.getText(0, doc.getLength());
143

    
144
                //  The lines affected by the latest document update
145

    
146
                int startLine = rootElement.getElementIndex( offset );
147
                int endLine = rootElement.getElementIndex( offset + length );
148

    
149
                //  Make sure all comment lines prior to the start line are commented
150
                //  and determine if the start line is still in a multi line comment
151

    
152
                setMultiLineComment( commentLinesBefore( content, startLine ) );
153

    
154
                //  Do the actual highlighting
155

    
156
                for (int i = startLine; i <= endLine; i++)
157
                {
158
                        applyHighlighting(content, i);
159
                }
160

    
161
                //  Resolve highlighting to the next end multi line delimiter
162

    
163
                if (isMultiLineComment())
164
                        commentLinesAfter(content, endLine);
165
                else
166
                        highlightLinesAfter(content, endLine);
167
        }
168

    
169
        /*
170
         *  Highlight lines when a multi line comment is still 'open'
171
         *  (ie. matching end delimiter has not yet been encountered)
172
         */
173
        private boolean commentLinesBefore(String content, int line)
174
        {
175
                int offset = rootElement.getElement( line ).getStartOffset();
176

    
177
                //  Start of comment not found, nothing to do
178

    
179
                int startDelimiter = lastIndexOf( content, getStartDelimiter(), offset - 2 );
180

    
181
                if (startDelimiter < 0)
182
                        return false;
183

    
184
                //  Matching start/end of comment found, nothing to do
185

    
186
                int endDelimiter = indexOf( content, getEndDelimiter(), startDelimiter );
187

    
188
                if (endDelimiter < offset & endDelimiter != -1)
189
                        return false;
190

    
191
                //  End of comment not found, highlight the lines
192

    
193
                doc.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1, comment, false);
194
                return true;
195
        }
196

    
197
        /*
198
         *  Highlight comment lines to matching end delimiter
199
         */
200
        private void commentLinesAfter(String content, int line)
201
        {
202
                int offset = rootElement.getElement( line ).getEndOffset();
203

    
204
                //  End of comment not found, nothing to do
205

    
206
                int endDelimiter = indexOf( content, getEndDelimiter(), offset );
207

    
208
                if (endDelimiter < 0)
209
                        return;
210

    
211
                //  Matching start/end of comment found, comment the lines
212

    
213
                int startDelimiter = lastIndexOf( content, getStartDelimiter(), endDelimiter );
214

    
215
                if (startDelimiter < 0 || startDelimiter <= offset)
216
                {
217
                        doc.setCharacterAttributes(offset, endDelimiter - offset + 1, comment, false);
218
                }
219
        }
220

    
221
        /*
222
         *  Highlight lines to start or end delimiter
223
         */
224
        private void highlightLinesAfter(String content, int line)
225
                throws BadLocationException
226
        {
227
                int offset = rootElement.getElement( line ).getEndOffset();
228

    
229
                //  Start/End delimiter not found, nothing to do
230

    
231
                int startDelimiter = indexOf( content, getStartDelimiter(), offset );
232
                int endDelimiter = indexOf( content, getEndDelimiter(), offset );
233

    
234
                if (startDelimiter < 0)
235
                        startDelimiter = content.length();
236

    
237
                if (endDelimiter < 0)
238
                        endDelimiter = content.length();
239

    
240
                int delimiter = Math.min(startDelimiter, endDelimiter);
241

    
242
                if (delimiter < offset)
243
                        return;
244

    
245
                //        Start/End delimiter found, reapply highlighting
246

    
247
                int endLine = rootElement.getElementIndex( delimiter );
248

    
249
                for (int i = line + 1; i < endLine; i++)
250
                {
251
                        Element branch = rootElement.getElement( i );
252
                        Element leaf = doc.getCharacterElement( branch.getStartOffset() );
253
                        AttributeSet as = leaf.getAttributes();
254

    
255
                        if ( as.isEqual(comment) )
256
                                applyHighlighting(content, i);
257
                }
258
        }
259

    
260
        /*
261
         *  Parse the line to determine the appropriate highlighting
262
         */
263
        private void applyHighlighting(String content, int line)
264
                throws BadLocationException
265
        {
266
                int startOffset = rootElement.getElement( line ).getStartOffset();
267
                int endOffset = rootElement.getElement( line ).getEndOffset() - 1;
268

    
269
                int lineLength = endOffset - startOffset;
270
                int contentLength = content.length();
271

    
272
                if (endOffset >= contentLength)
273
                        endOffset = contentLength - 1;
274

    
275
                //  check for multi line comments
276
                //  (always set the comment attribute for the entire line)
277

    
278
                if (endingMultiLineComment(content, startOffset, endOffset)
279
                ||  isMultiLineComment()
280
                ||  startingMultiLineComment(content, startOffset, endOffset) )
281
                {
282
                        doc.setCharacterAttributes(startOffset, endOffset - startOffset + 1, comment, false);
283
                        return;
284
                }
285

    
286
                //  set normal attributes for the line
287

    
288
                doc.setCharacterAttributes(startOffset, lineLength, normal, true);
289

    
290
                //  check for single line comment
291

    
292
                int index = content.indexOf(getSingleLineDelimiter(), startOffset);
293

    
294
                if ( (index > -1) && (index < endOffset) )
295
                {
296
                        doc.setCharacterAttributes(index, endOffset - index + 1, comment, false);
297
                        endOffset = index - 1;
298
                }
299

    
300
                //  check for tokens
301

    
302
                checkForTokens(content, startOffset, endOffset);
303
        }
304

    
305
        /*
306
         *  Does this line contain the start delimiter
307
         */
308
        private boolean startingMultiLineComment(String content, int startOffset, int endOffset)
309
                throws BadLocationException
310
        {
311
                int index = indexOf( content, getStartDelimiter(), startOffset );
312

    
313
                if ( (index < 0) || (index > endOffset) )
314
                        return false;
315
                else
316
                {
317
                        setMultiLineComment( true );
318
                        return true;
319
                }
320
        }
321

    
322
        /*
323
         *  Does this line contain the end delimiter
324
         */
325
        private boolean endingMultiLineComment(String content, int startOffset, int endOffset)
326
                throws BadLocationException
327
        {
328
                int index = indexOf( content, getEndDelimiter(), startOffset );
329

    
330
                if ( (index < 0) || (index > endOffset) )
331
                        return false;
332
                else
333
                {
334
                        setMultiLineComment( false );
335
                        return true;
336
                }
337
        }
338

    
339
        /*
340
         *  We have found a start delimiter
341
         *  and are still searching for the end delimiter
342
         */
343
        private boolean isMultiLineComment()
344
        {
345
                return multiLineComment;
346
        }
347

    
348
        private void setMultiLineComment(boolean value)
349
        {
350
                multiLineComment = value;
351
        }
352

    
353
        /*
354
         *        Parse the line for tokens to highlight
355
         */
356
        private void checkForTokens(String content, int startOffset, int endOffset)
357
        {
358
                while (startOffset <= endOffset)
359
                {
360
                        //  skip the delimiters to find the start of a new token
361

    
362
                        while ( isDelimiter( content.substring(startOffset, startOffset + 1) ) )
363
                        {
364
                                if (startOffset < endOffset)
365
                                        startOffset++;
366
                                else
367
                                        return;
368
                        }
369

    
370
                        //  Extract and process the entire token
371

    
372
                        if ( isQuoteDelimiter( content.substring(startOffset, startOffset + 1) ) )
373
                                startOffset = getQuoteToken(content, startOffset, endOffset);
374
                        else
375
                                startOffset = getOtherToken(content, startOffset, endOffset);
376
                }
377
        }
378

    
379
        /*
380
         *
381
         */
382
        private int getQuoteToken(String content, int startOffset, int endOffset)
383
        {
384
                String quoteDelimiter = content.substring(startOffset, startOffset + 1);
385
                String escapeString = getEscapeString(quoteDelimiter);
386

    
387
                int index;
388
                int endOfQuote = startOffset;
389

    
390
                //  skip over the escape quotes in this quote
391

    
392
                index = content.indexOf(escapeString, endOfQuote + 1);
393

    
394
                while ( (index > -1) && (index < endOffset) )
395
                {
396
                        endOfQuote = index + 1;
397
                        index = content.indexOf(escapeString, endOfQuote);
398
                }
399

    
400
                // now find the matching delimiter
401

    
402
                index = content.indexOf(quoteDelimiter, endOfQuote + 1);
403

    
404
                if ( (index < 0) || (index > endOffset) )
405
                        endOfQuote = endOffset;
406
                else
407
                        endOfQuote = index;
408

    
409
                doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, quote, false);
410

    
411
                return endOfQuote + 1;
412
        }
413

    
414
        /*
415
         *
416
         */
417
        private int getOtherToken(String content, int startOffset, int endOffset)
418
        {
419
                int endOfToken = startOffset + 1;
420

    
421
                while ( endOfToken <= endOffset )
422
                {
423
                        if ( isDelimiter( content.substring(endOfToken, endOfToken + 1) ) )
424
                                break;
425

    
426
                        endOfToken++;
427
                }
428

    
429
                String token = content.substring(startOffset, endOfToken);
430

    
431
                if ( isKeyword( token ) )
432
                {
433
                        doc.setCharacterAttributes(startOffset, endOfToken - startOffset, keyword, false);
434
                }
435

    
436
                return endOfToken + 1;
437
        }
438

    
439
        /*
440
         *  Assume the needle will the found at the start/end of the line
441
         */
442
        private int indexOf(String content, String needle, int offset)
443
        {
444
                int index;
445

    
446
                while ( (index = content.indexOf(needle, offset)) != -1 )
447
                {
448
                        String text = getLine( content, index ).trim();
449

    
450
                        if (text.startsWith(needle) || text.endsWith(needle))
451
                                break;
452
                        else
453
                                offset = index + 1;
454
                }
455

    
456
                return index;
457
        }
458

    
459
        /*
460
         *  Assume the needle will the found at the start/end of the line
461
         */
462
        private int lastIndexOf(String content, String needle, int offset)
463
        {
464
                int index;
465

    
466
                while ( (index = content.lastIndexOf(needle, offset)) != -1 )
467
                {
468
                        String text = getLine( content, index ).trim();
469

    
470
                        if (text.startsWith(needle) || text.endsWith(needle))
471
                                break;
472
                        else
473
                                offset = index - 1;
474
                }
475

    
476
                return index;
477
        }
478

    
479
        private String getLine(String content, int offset)
480
        {
481
                int line = rootElement.getElementIndex( offset );
482
                Element lineElement = rootElement.getElement( line );
483
                int start = lineElement.getStartOffset();
484
                int end = lineElement.getEndOffset();
485
                return content.substring(start, end - 1);
486
        }
487

    
488
        /*
489
         *  Override for other languages
490
         */
491
        protected boolean isDelimiter(String character)
492
        {
493
                String operands = ";:{}()[]+-/%<=>!&|^~*";
494

    
495
                if (Character.isWhitespace( character.charAt(0) ) ||
496
                        operands.indexOf(character) != -1 )
497
                        return true;
498
                else
499
                        return false;
500
        }
501

    
502
        /*
503
         *  Override for other languages
504
         */
505
        protected boolean isQuoteDelimiter(String character)
506
        {
507
                String quoteDelimiters = "\"'";
508

    
509
                if (quoteDelimiters.indexOf(character) < 0)
510
                        return false;
511
                else
512
                        return true;
513
        }
514

    
515
        /*
516
         *  Override for other languages
517
         */
518
        protected boolean isKeyword(String token)
519
        {
520
                return keywords.contains( token );
521
        }
522

    
523
        /*
524
         *  Override for other languages
525
         */
526
        protected String getStartDelimiter()
527
        {
528
                return "/*";
529
        }
530

    
531
        /*
532
         *  Override for other languages
533
         */
534
        protected String getEndDelimiter()
535
        {
536
                return "*/";
537
        }
538

    
539
        /*
540
         *  Override for other languages
541
         */
542
        protected String getSingleLineDelimiter()
543
        {
544
                return "//";
545
        }
546

    
547
        /*
548
         *  Override for other languages
549
         */
550
        protected String getEscapeString(String quoteDelimiter)
551
        {
552
                return "\\" + quoteDelimiter;
553
        }
554

    
555
        /*
556
         *
557
         */
558
        protected String addMatchingBrace(int offset) throws BadLocationException
559
        {
560
                StringBuffer whiteSpace = new StringBuffer();
561
                int line = rootElement.getElementIndex( offset );
562
                int i = rootElement.getElement(line).getStartOffset();
563

    
564
                while (true)
565
                {
566
                        String temp = doc.getText(i, 1);
567

    
568
                        if (temp.equals(" ") || temp.equals("\t"))
569
                        {
570
                                whiteSpace.append(temp);
571
                                i++;
572
                        }
573
                        else
574
                                break;
575
                }
576

    
577
                return "{\n" + whiteSpace.toString() + "\t\n" + whiteSpace.toString() + "}";
578
        }
579

    
580
}
581