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 / PythonStyledDocument.java @ 212

History | View | Annotate | Download (14.7 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
public class PythonStyledDocument extends DefaultStyledDocument {
17
        /**
18
         * 
19
         */
20
        private static final long serialVersionUID = 2859530660266127826L;
21

    
22
        private final DefaultStyledDocument doc;
23
        private final Element rootElement;
24

    
25
        private boolean multiLineComment;
26
        private final MutableAttributeSet normal;
27
        private final MutableAttributeSet keyword;
28
        private final MutableAttributeSet comment;
29
        private final MutableAttributeSet quote;
30
        private final MutableAttributeSet identifiers;
31
        
32
    private final Set<String> keywords;
33

    
34
        public PythonStyledDocument() {
35
                doc = this;
36
                rootElement = doc.getDefaultRootElement();
37
                putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");
38
                
39
                int fontSize = 14;
40

    
41
                normal = new SimpleAttributeSet();
42
                StyleConstants.setForeground(normal, Color.black);
43
                StyleConstants.setFontFamily(normal, "Monospace");
44
                StyleConstants.setBold(normal, true);
45
                StyleConstants.setFontSize(normal, fontSize);
46

    
47
                comment = new SimpleAttributeSet();
48
                StyleConstants.setForeground(comment,new Color(63,127,95));
49
                //StyleConstants.setItalic(comment, true);
50
                StyleConstants.setBold(comment, true);
51
                StyleConstants.setFontFamily(comment, "Monospace");
52
                StyleConstants.setFontSize(comment, fontSize);
53

    
54

    
55
                keyword = new SimpleAttributeSet();
56
                StyleConstants.setForeground(keyword, new Color(127,22,145));
57
                StyleConstants.setFontFamily(keyword, "Monospace");
58
                StyleConstants.setBold(keyword, true);
59
                StyleConstants.setFontSize(keyword, fontSize);
60

    
61
                identifiers = new SimpleAttributeSet();
62
                StyleConstants.setForeground(identifiers, new Color(6,42,226));
63
                StyleConstants.setFontFamily(identifiers, "Monospace");
64
                StyleConstants.setBold(identifiers, true);
65
                StyleConstants.setFontSize(identifiers, fontSize);
66

    
67
                
68
                quote = new SimpleAttributeSet();
69
                StyleConstants.setForeground(quote,  new Color(176,3,3));
70
                StyleConstants.setFontFamily(quote, "Monospace");
71
                StyleConstants.setBold(quote, true);
72
                StyleConstants.setFontSize(quote, fontSize);
73
                
74
        keywords = new HashSet<String>();
75
                keywords.add("except");
76
                keywords.add("class");
77
                keywords.add("continue");
78
                keywords.add("default");
79
                keywords.add("else");
80
                keywords.add("finally");
81
                keywords.add("for");
82
                keywords.add("future");
83
                keywords.add("if");
84
                keywords.add("import");
85
                keywords.add("return");
86
                keywords.add("raise");
87
                keywords.add("try");
88
                keywords.add("while");
89
                keywords.add("vars");
90
                keywords.add("def");
91
                keywords.add("print");
92
                keywords.add("globals");
93

    
94

    
95
                keywords.add("True");
96
                keywords.add("False");
97
                keywords.add("None");
98

    
99
                keywords.add("self");
100
                keywords.add("super");
101

    
102
                keywords.add("int");
103
                keywords.add("unicode");
104
                keywords.add("str");
105
                keywords.add("double");
106
                keywords.add("float");
107
                keywords.add("long");
108
                keywords.add("list");
109
                keywords.add("tuple");
110
                keywords.add("dict");
111
                keywords.add("object");
112
                keywords.add("file");
113
                keywords.add("iter");
114

    
115
                keywords.add("isinstance");
116
                keywords.add("dir");
117
                keywords.add("repr");
118
                keywords.add("reduce");
119
                keywords.add("zip");
120
                keywords.add("chr");
121
                keywords.add("hex");
122
                keywords.add("id");
123
                keywords.add("max");
124
                keywords.add("min");
125
                keywords.add("oct");
126
                keywords.add("pow");
127
                keywords.add("org");
128
                keywords.add("set");
129
                keywords.add("staticmethod");
130
                keywords.add("sum");
131
                keywords.add("unichr");
132
                keywords.add("range");
133
                keywords.add("xrange");
134
                keywords.add("abs");
135
                keywords.add("len");
136
                keywords.add("len");
137
                keywords.add("apply");
138
                keywords.add("open");
139
                keywords.add("getattr");
140
                keywords.add("setattr");
141
                keywords.add("hasattr");
142

    
143

    
144

    
145
        }
146

    
147
        /*
148
         * Override to apply syntax highlighting after the document has been updated
149
         */
150
        @Override
151
    public void insertString(int offset, String str, AttributeSet a)
152
                        throws BadLocationException {
153
                super.insertString(offset, str, a);
154
                processChangedLines(offset, str.length());
155
        }
156

    
157
        /*
158
         * Override to apply syntax highlighting after the document has been updated
159
         */
160
        @Override
161
    public void remove(int offset, int length) throws BadLocationException {
162
                super.remove(offset, length);
163
                processChangedLines(offset, 0);
164
        }
165

    
166
        /*
167
         * Determine how many lines have been changed, then apply highlighting to
168
         * each line
169
         */
170
        public void processChangedLines(int offset, int length)
171
                        throws BadLocationException {
172
                String content = doc.getText(0, doc.getLength());
173

    
174
                // The lines affected by the latest document update
175

    
176
                int startLine = rootElement.getElementIndex(offset);
177
                int endLine = rootElement.getElementIndex(offset + length);
178

    
179
                // Make sure all comment lines prior to the start line are commented
180
                // and determine if the start line is still in a multi line comment
181

    
182
                setMultiLineComment(commentLinesBefore(content, startLine));
183

    
184
                // Do the actual highlighting
185

    
186
                for (int i = startLine; i <= endLine; i++) {
187
                        applyHighlighting(content, i);
188
                }
189

    
190
                // Resolve highlighting to the next end multi line delimiter
191

    
192
                if (isMultiLineComment())
193
                        commentLinesAfter(content, endLine);
194
                else
195
                        highlightLinesAfter(content, endLine);
196
        }
197

    
198
        /*
199
         * Highlight lines when a multi line comment is still 'open' (ie. matching
200
         * end delimiter has not yet been encountered)
201
         */
202
        private boolean commentLinesBefore(String content, int line) {
203
                int offset = rootElement.getElement(line).getStartOffset();
204

    
205
                // Start of comment not found, nothing to do
206

    
207
                int startDelimiter = lastIndexOf(content, getStartDelimiter(),
208
                                offset - 2);
209

    
210
                if (startDelimiter < 0)
211
                        return false;
212

    
213
                // Matching start/end of comment found, nothing to do
214

    
215
                int endDelimiter = indexOf(content, getEndDelimiter(), startDelimiter);
216

    
217
                if (endDelimiter < offset & endDelimiter != -1)
218
                        return false;
219

    
220
                // End of comment not found, highlight the lines
221

    
222
                doc.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1,
223
                                comment, false);
224
                return true;
225
        }
226

    
227
        /*
228
         * Highlight comment lines to matching end delimiter
229
         */
230
        private void commentLinesAfter(String content, int line) {
231
                int offset = rootElement.getElement(line).getEndOffset();
232

    
233
                // End of comment not found, nothing to do
234

    
235
                int endDelimiter = indexOf(content, getEndDelimiter(), offset);
236

    
237
                if (endDelimiter < 0)
238
                        return;
239

    
240
                // Matching start/end of comment found, comment the lines
241

    
242
                int startDelimiter = lastIndexOf(content, getStartDelimiter(),
243
                                endDelimiter);
244

    
245
                if (startDelimiter < 0 || startDelimiter <= offset) {
246
                        doc.setCharacterAttributes(offset, endDelimiter - offset + 1,
247
                                        comment, false);
248
                }
249
        }
250

    
251
        /*
252
         * Highlight lines to start or end delimiter
253
         */
254
        private void highlightLinesAfter(String content, int line)
255
                        throws BadLocationException {
256
                int offset = rootElement.getElement(line).getEndOffset();
257

    
258
                // Start/End delimiter not found, nothing to do
259

    
260
                int startDelimiter = indexOf(content, getStartDelimiter(), offset);
261
                int endDelimiter = indexOf(content, getEndDelimiter(), offset);
262

    
263
                if (startDelimiter < 0)
264
                        startDelimiter = content.length();
265

    
266
                if (endDelimiter < 0)
267
                        endDelimiter = content.length();
268

    
269
                int delimiter = Math.min(startDelimiter, endDelimiter);
270

    
271
                if (delimiter < offset)
272
                        return;
273

    
274
                // Start/End delimiter found, reapply highlighting
275

    
276
                int endLine = rootElement.getElementIndex(delimiter);
277

    
278
                for (int i = line + 1; i < endLine; i++) {
279
                        Element branch = rootElement.getElement(i);
280
                        Element leaf = doc.getCharacterElement(branch.getStartOffset());
281
                        AttributeSet as = leaf.getAttributes();
282

    
283
                        if (as.isEqual(comment))
284
                                applyHighlighting(content, i);
285
                }
286
        }
287

    
288
        /*
289
         * Parse the line to determine the appropriate highlighting
290
         */
291
        private void applyHighlighting(String content, int line)
292
                        throws BadLocationException {
293
                int startOffset = rootElement.getElement(line).getStartOffset();
294
                int endOffset = rootElement.getElement(line).getEndOffset() - 1;
295

    
296
                int lineLength = endOffset - startOffset;
297
                int contentLength = content.length();
298

    
299
                if (endOffset >= contentLength)
300
                        endOffset = contentLength - 1;
301

    
302
                // check for multi line comments
303
                // (always set the comment attribute for the entire line)
304

    
305
                if (endingMultiLineComment(content, startOffset, endOffset)
306
                                || isMultiLineComment()
307
                                || startingMultiLineComment(content, startOffset, endOffset)) {
308
                        doc.setCharacterAttributes(startOffset,
309
                                        endOffset - startOffset + 1, comment, false);
310
                        return;
311
                }
312

    
313
                // set normal attributes for the line
314

    
315
                doc.setCharacterAttributes(startOffset, lineLength, normal, true);
316

    
317
                // check for single line comment
318

    
319
                int index = content.indexOf(getSingleLineDelimiter(), startOffset);
320

    
321
                if ((index > -1) && (index < endOffset)) {
322
                        doc.setCharacterAttributes(index, endOffset - index + 1, comment,
323
                                        false);
324
                        endOffset = index - 1;
325
                }
326

    
327
                // check for tokens
328

    
329
                checkForTokens(content, startOffset, endOffset);
330
        }
331

    
332
        /*
333
         * Does this line contain the start delimiter
334
         */
335
        private boolean startingMultiLineComment(String content, int startOffset,
336
                        int endOffset) throws BadLocationException {
337
                int index = indexOf(content, getStartDelimiter(), startOffset);
338

    
339
                if ((index < 0) || (index > endOffset))
340
                        return false;
341
                else {
342
                        setMultiLineComment(true);
343
                        return true;
344
                }
345
        }
346

    
347
        /*
348
         * Does this line contain the end delimiter
349
         */
350
        private boolean endingMultiLineComment(String content, int startOffset,
351
                        int endOffset) throws BadLocationException {
352
                int index = indexOf(content, getEndDelimiter(), startOffset);
353

    
354
                if ((index < 0) || (index > endOffset))
355
                        return false;
356
                else {
357
                        setMultiLineComment(false);
358
                        return true;
359
                }
360
        }
361

    
362
        /*
363
         * We have found a start delimiter and are still searching for the end
364
         * delimiter
365
         */
366
        private boolean isMultiLineComment() {
367
                return multiLineComment;
368
        }
369

    
370
        private void setMultiLineComment(boolean value) {
371
                multiLineComment = value;
372
        }
373

    
374
        /*
375
         * Parse the line for tokens to highlight
376
         */
377
        private void checkForTokens(String content, int startOffset, int endOffset) {
378
                while (startOffset <= endOffset) {
379
                        // skip the delimiters to find the start of a new token
380

    
381
                        while (isDelimiter(content.substring(startOffset, startOffset + 1))) {
382
                                if (startOffset < endOffset)
383
                                        startOffset++;
384
                                else
385
                                        return;
386
                        }
387

    
388
                        // Extract and process the entire token
389

    
390
                        if (isQuoteDelimiter(content
391
                                        .substring(startOffset, startOffset + 1)))
392
                                startOffset = getQuoteToken(content, startOffset, endOffset);
393
                        else
394
                                startOffset = getOtherToken(content, startOffset, endOffset);
395
                }
396
        }
397

    
398
        /*
399
         *
400
         */
401
        private int getQuoteToken(String content, int startOffset, int endOffset) {
402
                String quoteDelimiter = content.substring(startOffset, startOffset + 1);
403
                String escapeString = getEscapeString(quoteDelimiter);
404

    
405
                int index;
406
                int endOfQuote = startOffset;
407

    
408
                // skip over the escape quotes in this quote
409

    
410
                index = content.indexOf(escapeString, endOfQuote + 1);
411

    
412
                while ((index > -1) && (index < endOffset)) {
413
                        endOfQuote = index + 1;
414
                        index = content.indexOf(escapeString, endOfQuote);
415
                }
416

    
417
                // now find the matching delimiter
418

    
419
                index = content.indexOf(quoteDelimiter, endOfQuote + 1);
420

    
421
                if ((index < 0) || (index > endOffset))
422
                        endOfQuote = endOffset;
423
                else
424
                        endOfQuote = index;
425

    
426
                doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1,
427
                                quote, false);
428

    
429
                return endOfQuote + 1;
430
        }
431

    
432
        /*
433
         *
434
         */
435
        private int getOtherToken(String content, int startOffset, int endOffset) {
436
                int endOfToken = startOffset + 1;
437

    
438
                while (endOfToken <= endOffset) {
439
                        if (isDelimiter(content.substring(endOfToken, endOfToken + 1)))
440
                                break;
441

    
442
                        endOfToken++;
443
                }
444

    
445
                String token = content.substring(startOffset, endOfToken);
446

    
447
                if (isKeyword(token)) {
448
                        doc.setCharacterAttributes(startOffset, endOfToken - startOffset,
449
                                        keyword, false);
450
                }else if (isIdentifier(token)) {
451
                        doc.setCharacterAttributes(startOffset, endOfToken - startOffset,
452
                                identifiers, false);
453
                }
454
                                
455

    
456
                return endOfToken + 1;
457
        }
458

    
459
        private boolean isIdentifier(String token) {
460
                return token.matches("[A-Za-z_][A-Za-z0-9_]*");
461
        }
462

    
463
        /*
464
         * Assume the needle will the found at the start/end of the line
465
         */
466
        private int indexOf(String content, String needle, int offset) {
467
                int index;
468

    
469
                while ((index = content.indexOf(needle, offset)) != -1) {
470
                        String text = getLine(content, index).trim();
471

    
472
                        if (text.startsWith(needle) || text.endsWith(needle))
473
                                break;
474
                        else
475
                                offset = index + 1;
476
                }
477

    
478
                return index;
479
        }
480

    
481
        /*
482
         * Assume the needle will the found at the start/end of the line
483
         */
484
        private int lastIndexOf(String content, String needle, int offset) {
485
                int index;
486

    
487
                while ((index = content.lastIndexOf(needle, offset)) != -1) {
488
                        String text = getLine(content, index).trim();
489

    
490
                        if (text.startsWith(needle) || text.endsWith(needle))
491
                                break;
492
                        else
493
                                offset = index - 1;
494
                }
495

    
496
                return index;
497
        }
498

    
499
        private String getLine(String content, int offset) {
500
                int line = rootElement.getElementIndex(offset);
501
                Element lineElement = rootElement.getElement(line);
502
                int start = lineElement.getStartOffset();
503
                int end = lineElement.getEndOffset();
504
                return content.substring(start, end - 1);
505
        }
506

    
507
        /*
508
         * Override for other languages
509
         */
510
        protected boolean isDelimiter(String character) {
511
                String operands = ";:()[]+-/%<=>!&|^~*";
512

    
513
                if (Character.isWhitespace(character.charAt(0))
514
                                || operands.indexOf(character) != -1)
515
                        return true;
516
                else
517
                        return false;
518
        }
519

    
520
        /*
521
         * Override for other languages
522
         */
523
        protected boolean isQuoteDelimiter(String character) {
524
                String quoteDelimiters = "\"'";
525

    
526
                if (quoteDelimiters.indexOf(character) < 0)
527
                        return false;
528
                else
529
                        return true;
530
        }
531

    
532
        /*
533
         * Override for other languages
534
         */
535
        protected boolean isKeyword(String token) {
536
                return keywords.contains(token);
537
        }
538

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

    
546
        /*
547
         * Override for other languages
548
         */
549
        protected String getEndDelimiter() {
550
                return "\"\"\"";
551
        }
552

    
553
        /*
554
         * Override for other languages
555
         */
556
        protected String getSingleLineDelimiter() {
557
                return "#";
558
        }
559

    
560
        /*
561
         * Override for other languages
562
         */
563
        protected String getEscapeString(String quoteDelimiter) {
564
                return "\\" + quoteDelimiter;
565
        }
566

    
567
        /*
568
         *
569
        protected String addMatchingBrace(int offset) throws BadLocationException {
570
                StringBuffer whiteSpace = new StringBuffer();
571
                int line = rootElement.getElementIndex(offset);
572
                int i = rootElement.getElement(line).getStartOffset();
573

574
                while (true) {
575
                        String temp = doc.getText(i, 1);
576

577
                        if (temp.equals(" ") || temp.equals("\t")) {
578
                                whiteSpace.append(temp);
579
                                i++;
580
                        } else
581
                                break;
582
                }
583

584
                return "{\n" + whiteSpace.toString() + "\t\n" + whiteSpace.toString()
585
                                + "}";
586
        }
587
         */
588

    
589
}
590

    
591