source: src/main/java/weka/gui/scripting/SyntaxDocument.java @ 9

Last change on this file since 9 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 32.9 KB
Line 
1// Filename: SyntaxDocument.java
2// http://forums.sun.com/thread.jspa?threadID=743407&messageID=9497681
3// http://www.dound.com/src/MultiSyntaxDocument.java
4
5/**
6 * (C) camickr (primary author; java sun forums user)
7 * (C) David Underhill
8 * (C) 2009 University of Waikato, Hamilton, New Zealand
9 */
10
11package weka.gui.scripting;
12
13import weka.gui.visualize.VisualizeUtils;
14
15import java.awt.Color;
16import java.awt.Font;
17import java.awt.FontMetrics;
18import java.awt.Toolkit;
19import java.util.HashMap;
20import java.util.Properties;
21
22import javax.swing.text.AttributeSet;
23import javax.swing.text.BadLocationException;
24import javax.swing.text.DefaultEditorKit;
25import javax.swing.text.DefaultStyledDocument;
26import javax.swing.text.Element;
27import javax.swing.text.MutableAttributeSet;
28import javax.swing.text.SimpleAttributeSet;
29import javax.swing.text.StyleConstants;
30import javax.swing.text.TabSet;
31import javax.swing.text.TabStop;
32
33/**
34 * Highlights syntax in a DefaultStyledDocument. Allows any number of keywords
35 * to be formatted in any number of user-defined styles.
36 *
37 * @author camickr (primary author; java sun forums user)
38 * @author David Underhill
39 * @author FracPete (fracpete at waikato dot ac dot nz) - use of a properties file to setup syntax highlighting instead of hard-coded
40 */
41public class SyntaxDocument
42  extends DefaultStyledDocument {
43
44  /** for serialization. */
45  protected static final long serialVersionUID = -3642426465631271381L;
46
47  /** the maximum number of tabs. */
48  public static final int MAX_TABS = 35;
49 
50  /** the font family. */
51  public static final String DEFAULT_FONT_FAMILY = "monospaced";
52
53  /** the font size. */
54  public static final int DEFAULT_FONT_SIZE = 12;
55
56  /** the attribute set for normal code. */
57  public static final SimpleAttributeSet DEFAULT_NORMAL;
58
59  /** the attribute set for comments. */
60  public static final SimpleAttributeSet DEFAULT_COMMENT;
61
62  /** the attribute set for strings. */
63  public static final SimpleAttributeSet DEFAULT_STRING;
64
65  /** the attribute set for keywords. */
66  public static final SimpleAttributeSet DEFAULT_KEYWORD;
67
68  static {
69    DEFAULT_NORMAL = new SimpleAttributeSet();
70    StyleConstants.setForeground(DEFAULT_NORMAL, Color.BLACK);
71    StyleConstants.setFontFamily(DEFAULT_NORMAL, DEFAULT_FONT_FAMILY);
72    StyleConstants.setFontSize(DEFAULT_NORMAL, DEFAULT_FONT_SIZE);
73
74    DEFAULT_COMMENT = new SimpleAttributeSet();
75    StyleConstants.setForeground(DEFAULT_COMMENT, Color.GRAY);
76    StyleConstants.setFontFamily(DEFAULT_COMMENT, DEFAULT_FONT_FAMILY);
77    StyleConstants.setFontSize(DEFAULT_COMMENT, DEFAULT_FONT_SIZE);
78
79    DEFAULT_STRING = new SimpleAttributeSet();
80    StyleConstants.setForeground(DEFAULT_STRING, Color.RED);
81    StyleConstants.setFontFamily(DEFAULT_STRING, DEFAULT_FONT_FAMILY);
82    StyleConstants.setFontSize(DEFAULT_STRING, DEFAULT_FONT_SIZE);
83
84    // default style for new keyword types
85    DEFAULT_KEYWORD = new SimpleAttributeSet();
86    StyleConstants.setForeground(DEFAULT_KEYWORD, Color.BLUE);
87    StyleConstants.setBold(DEFAULT_KEYWORD, false);
88    StyleConstants.setFontFamily(DEFAULT_KEYWORD, DEFAULT_FONT_FAMILY);
89    StyleConstants.setFontSize(DEFAULT_KEYWORD, DEFAULT_FONT_SIZE);
90  }
91
92  /**
93   * The attribute type.
94   */
95  public enum ATTR_TYPE {
96    /** normal string. */
97    Normal,
98    /** a comment. */
99    Comment,
100    /** a quoted string. */
101    Quote;
102  }
103
104  /** the document. */
105  protected DefaultStyledDocument m_Self;
106
107  /** the root element. */
108  protected Element m_RootElement;
109
110  /** whether we're currently in a multi-line comment. */
111  protected boolean m_InsideMultiLineComment;
112
113  /** the keywords. */
114  protected HashMap<String, MutableAttributeSet> m_Keywords;
115 
116  /** the delimiters. */
117  protected String m_Delimiters;
118 
119  /** the quote delimiter. */
120  protected String m_QuoteDelimiters;
121 
122  /** the quote escape. */
123  protected String m_QuoteEscape;
124 
125  /** the multi-line comment start. */
126  protected String m_MultiLineCommentStart;
127 
128  /** the multi-line comment end. */
129  protected String m_MultiLineCommentEnd;
130 
131  /** the single-line comment start. */
132  protected String m_SingleLineCommentStart;
133
134  /** the start of a block. */
135  protected String m_BlockStart;
136
137  /** the end of a block. */
138  protected String m_BlockEnd;
139 
140  /** the font size. */
141  protected int m_FontSize;
142
143  /** the font name. */
144  protected String m_FontName;
145 
146  /** the background color. */
147  protected Color m_BackgroundColor;
148 
149  /** the number of spaces used for indentation. */
150  protected String m_Indentation;
151
152  /** whether to add matching brackets. */
153  protected boolean m_AddMatchingEndBlocks;
154 
155  /** whether to use blanks instead of tabs. */
156  protected boolean m_UseBlanks;
157 
158  /** whether multi-line comments are enabled. */
159  protected boolean m_MultiLineComment;
160 
161  /** whether keywords are case-sensitive. */
162  protected boolean m_CaseSensitive;
163 
164  /**
165   * Initializes the document.
166   *
167   * @param props       the properties to obtain the setup from
168   */
169  public SyntaxDocument(Properties props) {
170    m_Self        = this;
171    m_RootElement = m_Self.getDefaultRootElement();
172    m_Keywords    = new HashMap<String, MutableAttributeSet>();
173    m_FontSize    = DEFAULT_FONT_SIZE;
174    m_FontName    = DEFAULT_FONT_FAMILY;
175    putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");
176   
177    setup(props);
178  }
179 
180  /**
181   * Sets up the document according to the properties.
182   *
183   * @param props       the properties to use
184   */
185  protected void setup(Properties props) {
186    setDelimiters(props.getProperty("Delimiters", ";:{}()[]+-/%<=>!&|^~*"));
187    setQuoteDelimiters(props.getProperty("QuoteDelimiters", "\"\'"));
188    setQuoteEscape(props.getProperty("QuoteEscape", "\\"));
189    setSingleLineCommentStart(props.getProperty("SingleLineCommentStart", "//"));
190    setMultiLineComment(props.getProperty("MultiLineComment", "false").equals("true"));
191    setMultiLineCommentStart(props.getProperty("MultiLineCommentStart", "/*"));
192    setMultiLineCommentEnd(props.getProperty("MultiLineCommentEnd", "*/"));
193    setBlockStart(props.getProperty("BlockStart", "{"));
194    setBlockEnd(props.getProperty("BlockEnd", "}"));
195    setAddMatchingEndBlocks(props.getProperty("AddMatchingBlockEnd", "false").equals("true"));
196    setUseBlanks(props.getProperty("UseBlanks", "false").equals("true"));
197    setCaseSensitive(props.getProperty("CaseSensitive", "true").equals("true"));
198    addKeywords(props.getProperty("Keywords", "").trim().replaceAll(" ", "").split(","), DEFAULT_KEYWORD);
199    setTabs(Integer.parseInt(props.getProperty("Tabs", "2")));
200    setAttributeColor(DEFAULT_NORMAL, VisualizeUtils.processColour(props.getProperty("ForegroundColor", "black"), Color.BLACK));
201    setAttributeColor(DEFAULT_COMMENT, VisualizeUtils.processColour(props.getProperty("CommentColor", "gray"), Color.GRAY));
202    setAttributeColor(DEFAULT_STRING, VisualizeUtils.processColour(props.getProperty("StringColor", "red"), Color.RED));
203    setAttributeColor(DEFAULT_KEYWORD, VisualizeUtils.processColour(props.getProperty("KeywordColor", "blue"), Color.BLUE));
204    setBackgroundColor(VisualizeUtils.processColour(props.getProperty("BackgroundColor", "white"), Color.WHITE));
205    setFontName(props.getProperty("FontName", "monospaced"));
206    setFontSize(Integer.parseInt(props.getProperty("FontSize", "12")));
207    setIndentationSize(Integer.parseInt(props.getProperty("Indentation", "2")));
208  }
209
210  /**
211   * Sets the font of the specified attribute.
212   *
213   * @param attr
214   *          the attribute to apply this font to (normal, comment, string)
215   * @param style
216   *          font style (Font.BOLD, Font.ITALIC, Font.PLAIN)
217   */
218  public void setAttributeFont(ATTR_TYPE attr, int style) {
219    Font f = new Font(m_FontName, style, m_FontSize);
220
221    if (attr == ATTR_TYPE.Comment)
222      setAttributeFont(DEFAULT_COMMENT, f);
223    else if (attr == ATTR_TYPE.Quote)
224      setAttributeFont(DEFAULT_STRING, f);
225    else
226      setAttributeFont(DEFAULT_NORMAL, f);
227  }
228
229  /**
230   * Sets the font of the specified attribute.
231   *
232   * @param attr
233   *          attribute to apply this font to
234   * @param f
235   *          the font to use
236   */
237  public static void setAttributeFont(MutableAttributeSet attr, Font f) {
238    StyleConstants.setBold(attr, f.isBold());
239    StyleConstants.setItalic(attr, f.isItalic());
240    StyleConstants.setFontFamily(attr, f.getFamily());
241    StyleConstants.setFontSize(attr, f.getSize());
242  }
243
244  /**
245   * Sets the foreground (font) color of the specified attribute.
246   *
247   * @param attr
248   *          the attribute to apply this font to (normal, comment, string)
249   * @param c
250   *          the color to use
251   */
252  public void setAttributeColor(ATTR_TYPE attr, Color c) {
253    if (attr == ATTR_TYPE.Comment)
254      setAttributeColor(DEFAULT_COMMENT, c);
255    else if (attr == ATTR_TYPE.Quote)
256      setAttributeColor(DEFAULT_STRING, c);
257    else
258      setAttributeColor(DEFAULT_NORMAL, c);
259  }
260
261  /**
262   * Sets the foreground (font) color of the specified attribute.
263   *
264   * @param attr
265   *          attribute to apply this color to
266   * @param c
267   *          the color to use
268   */
269  public static void setAttributeColor(MutableAttributeSet attr, Color c) {
270    StyleConstants.setForeground(attr, c);
271  }
272
273  /**
274   * Associates the keywords with a particular formatting style.
275   *
276   * @param keywords
277   *          the tokens or words to format
278   * @param attr
279   *          how to format the keywords
280   */
281  public void addKeywords(String[] keywords, MutableAttributeSet attr) {
282    int         i;
283   
284    for (i = 0; i < keywords.length; i++)
285      addKeyword(keywords[i], attr);
286  }
287 
288  /**
289   * Associates a keyword with a particular formatting style.
290   *
291   * @param keyword
292   *          the token or word to format
293   * @param attr
294   *          how to format keyword
295   */
296  public void addKeyword(String keyword, MutableAttributeSet attr) {
297    if (m_CaseSensitive)
298      m_Keywords.put(keyword, attr);
299    else
300      m_Keywords.put(keyword.toLowerCase(), attr);
301  }
302
303  /**
304   * Gets the formatting for a keyword.
305   *
306   * @param keyword
307   *          the token or word to stop formatting
308   *
309   * @return how keyword is formatted, or null if no formatting is applied to it
310   */
311  public MutableAttributeSet getKeywordFormatting(String keyword) {
312    if (m_CaseSensitive)
313      return m_Keywords.get(keyword);
314    else
315      return m_Keywords.get(keyword.toLowerCase());
316  }
317
318  /**
319   * Removes an association between a keyword with a particular formatting style.
320   *
321   * @param keyword
322   *          the token or word to stop formatting
323   */
324  public void removeKeyword(String keyword) {
325    if (m_CaseSensitive)
326      m_Keywords.remove(keyword);
327    else
328      m_Keywords.remove(keyword.toLowerCase());
329  }
330
331  /**
332   * sets the number of characters per tab.
333   *
334   * @param charactersPerTab
335   *            the characters per tab
336   */
337  public void setTabs(int charactersPerTab) {
338    Font f = new Font(m_FontName, Font.PLAIN, m_FontSize);
339
340    FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f);
341    int charWidth = fm.charWidth('w');
342    int tabWidth = charWidth * charactersPerTab;
343
344    TabStop[] tabs = new TabStop[MAX_TABS];
345
346    for (int j = 0; j < tabs.length; j++)
347      tabs[j] = new TabStop((j+1) * tabWidth);
348
349    TabSet tabSet = new TabSet(tabs);
350    SimpleAttributeSet attributes = new SimpleAttributeSet();
351    StyleConstants.setTabSet(attributes, tabSet);
352    int length = getLength();
353    setParagraphAttributes(0, length, attributes, false);
354  }
355
356  /**
357   * Override to apply syntax highlighting after the document has been updated.
358   *
359   * @param offset
360   *            the offset
361   * @param str
362   *            the string to insert
363   * @param a
364   *            the attribute set, can be null
365   * @throws BadLocationException
366   *            if offset is invalid
367   */
368  public void insertString(int offset, String str, AttributeSet a)
369      throws BadLocationException {
370    if (m_AddMatchingEndBlocks && (m_BlockStart.length() > 0) && str.equals(m_BlockStart))
371      str = addMatchingBlockEnd(offset);
372    else if (m_UseBlanks && str.equals("\t"))
373      str = m_Indentation;
374   
375    super.insertString(offset, str, a);
376    processChangedLines(offset, str.length());
377  }
378
379  /**
380   * Applies syntax highlighting after the document has been updated.
381   *
382   * @param offset
383   *            the offset of the deletion
384   * @param length
385   *            the length of the deletion
386   * @throws BadLocationException
387   *            if offsets are invalid
388   */
389  public void remove(int offset, int length) throws BadLocationException {
390    super.remove(offset, length);
391    processChangedLines(offset, 0);
392  }
393
394  /**
395   * Determine how many lines have been changed, then apply highlighting to each
396   * line.
397   *
398   * @param offset
399   *            the offset of the changed lines
400   * @param length
401   *            the length of the change
402   * @throws BadLocationException
403   *            if offset is invalid
404   */
405  public void processChangedLines(int offset, int length)
406      throws BadLocationException {
407    String content = m_Self.getText(0, m_Self.getLength());
408
409    // The lines affected by the latest document update
410
411    int startLine = m_RootElement.getElementIndex(offset);
412    int endLine = m_RootElement.getElementIndex(offset + length);
413
414    // Make sure all comment lines prior to the start line are commented
415    // and determine if the start line is still in a multi line comment
416
417    if (getMultiLineComment())
418      setInsideMultiLineComment(commentLinesBefore(content, startLine));
419
420    // Do the actual highlighting
421
422    for (int i = startLine; i <= endLine; i++) {
423      applyHighlighting(content, i);
424    }
425
426    // Resolve highlighting to the next end multi line delimiter
427
428    if (isMultiLineComment())
429      commentLinesAfter(content, endLine);
430    else
431      highlightLinesAfter(content, endLine);
432  }
433
434  /**
435   * Highlight lines when a multi line comment is still 'open' (ie. matching end
436   * delimiter has not yet been encountered).
437   *
438   * @param content
439   *            the content to check
440   * @param line
441   *            the line number
442   * @return
443   *            true if there are comment lines before
444   */
445  protected boolean commentLinesBefore(String content, int line) {
446    int offset = m_RootElement.getElement(line).getStartOffset();
447
448    // Start of comment not found, nothing to do
449
450    int startDelimiter = -1;
451    if (getMultiLineComment())
452      startDelimiter = lastIndexOf(content, getMultiLineCommentStart(), offset - 2);
453
454    if (startDelimiter < 0)
455      return false;
456
457    // Matching start/end of comment found, nothing to do
458
459    int endDelimiter = indexOf(content, getMultiLineCommentEnd(), startDelimiter);
460
461    if (endDelimiter < offset & endDelimiter != -1)
462      return false;
463
464    // End of comment not found, highlight the lines
465
466    m_Self.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1,
467        DEFAULT_COMMENT, false);
468    return true;
469  }
470
471  /**
472   * Highlight comment lines to matching end delimiter.
473   *
474   * @param content
475   *            the content to parse
476   * @param line
477   *            the line number
478   */
479  protected void commentLinesAfter(String content, int line) {
480    int offset = m_RootElement.getElement(line).getEndOffset();
481
482    // End of comment not found, nothing to do
483
484    int endDelimiter = -1;
485    if (getMultiLineComment())
486      endDelimiter = indexOf(content, getMultiLineCommentEnd(), offset);
487
488    if (endDelimiter < 0)
489      return;
490
491    // Matching start/end of comment found, comment the lines
492
493    int startDelimiter = lastIndexOf(content, getMultiLineCommentStart(), endDelimiter);
494
495    if (startDelimiter < 0 || startDelimiter <= offset) {
496      m_Self.setCharacterAttributes(offset, endDelimiter - offset + 1, DEFAULT_COMMENT,
497          false);
498    }
499  }
500
501  /**
502   * Highlight lines to start or end delimiter.
503   *
504   * @param content
505   *            the content to parse
506   * @param line
507   *            the line number
508   * @throws BadLocationException
509   *            if offsets are wrong
510   */
511  protected void highlightLinesAfter(String content, int line)
512      throws BadLocationException {
513    int offset = m_RootElement.getElement(line).getEndOffset();
514
515    // Start/End delimiter not found, nothing to do
516
517    int startDelimiter = -1;
518    int endDelimiter = -1;
519    if (getMultiLineComment()) {
520      startDelimiter = indexOf(content, getMultiLineCommentStart(), offset);
521      endDelimiter = indexOf(content, getMultiLineCommentEnd(), offset);
522    }
523
524    if (startDelimiter < 0)
525      startDelimiter = content.length();
526
527    if (endDelimiter < 0)
528      endDelimiter = content.length();
529
530    int delimiter = Math.min(startDelimiter, endDelimiter);
531
532    if (delimiter < offset)
533      return;
534
535    // Start/End delimiter found, reapply highlighting
536
537    int endLine = m_RootElement.getElementIndex(delimiter);
538
539    for (int i = line + 1; i < endLine; i++) {
540      Element branch = m_RootElement.getElement(i);
541      Element leaf = m_Self.getCharacterElement(branch.getStartOffset());
542      AttributeSet as = leaf.getAttributes();
543
544      if (as.isEqual(DEFAULT_COMMENT))
545        applyHighlighting(content, i);
546    }
547  }
548
549  /**
550   * Parse the line to determine the appropriate highlighting.
551   *
552   * @param content
553   *            the content to parse
554   * @param line
555   *            the line number
556   * @throws BadLocationException
557   *            if offsets are invalid
558   */
559  protected void applyHighlighting(String content, int line)
560      throws BadLocationException {
561    int startOffset = m_RootElement.getElement(line).getStartOffset();
562    int endOffset = m_RootElement.getElement(line).getEndOffset() - 1;
563
564    int lineLength = endOffset - startOffset;
565    int contentLength = content.length();
566
567    if (endOffset >= contentLength)
568      endOffset = contentLength - 1;
569
570    // check for multi line comments
571    // (always set the comment attribute for the entire line)
572
573    if (getMultiLineComment()) {
574      if (endingMultiLineComment(content, startOffset, endOffset)
575          || isMultiLineComment()
576          || startingMultiLineComment(content, startOffset, endOffset)) {
577        m_Self.setCharacterAttributes(startOffset, endOffset - startOffset + 1,
578            DEFAULT_COMMENT, false);
579        return;
580      }
581    }
582
583    // set normal attributes for the line
584
585    m_Self.setCharacterAttributes(startOffset, lineLength, DEFAULT_NORMAL, true);
586
587    // check for single line comment
588
589    int index = content.indexOf(getSingleLineCommentStart(), startOffset);
590
591    if ((index > -1) && (index < endOffset)) {
592      m_Self.setCharacterAttributes(index, endOffset - index + 1, DEFAULT_COMMENT, false);
593      endOffset = index - 1;
594    }
595
596    // check for tokens
597
598    checkForTokens(content, startOffset, endOffset);
599  }
600
601  /**
602   * Does this line contain the start of a multi-line comment.
603   *
604   * @param content
605   *            the content to search
606   * @param startOffset
607   *            the start of the search
608   * @param endOffset
609   *            the end of the search
610   * @return
611   *            true if it contains the start delimiter
612   * @throws BadLocationException
613   *            if offsets are invalid
614   */
615  protected boolean startingMultiLineComment(String content, int startOffset,
616      int endOffset) throws BadLocationException {
617    if (!getMultiLineComment())
618      return false;
619   
620    int index = indexOf(content, getMultiLineCommentStart(), startOffset);
621
622    if ((index < 0) || (index > endOffset))
623      return false;
624    else {
625      setInsideMultiLineComment(true);
626      return true;
627    }
628  }
629
630  /**
631   * Does this line contain the end delimiter of a multi-line comment.
632   *
633   * @param content
634   *            the content to search
635   * @param startOffset
636   *            the start of the search
637   * @param endOffset
638   *            the end of the search
639   * @return
640   *            true if the line contains the end delimiter
641   * @throws BadLocationException
642   *            if offsets are invalid
643   */
644  protected boolean endingMultiLineComment(String content, int startOffset,
645      int endOffset) throws BadLocationException {
646    if (!getMultiLineComment())
647      return false;
648   
649    int index = indexOf(content, getMultiLineCommentEnd(), startOffset);
650
651    if ((index < 0) || (index > endOffset))
652      return false;
653    else {
654      setInsideMultiLineComment(false);
655      return true;
656    }
657  }
658
659  /**
660   * We have found a start delimiter and are still searching for the end
661   * delimiter.
662   *
663   * @return
664   *            true if currently within a multi-line comment
665   */
666  protected boolean isMultiLineComment() {
667    return m_InsideMultiLineComment;
668  }
669
670  /**
671   * Sets whether we're currently within a multi-line comment or not.
672   *
673   * @param value
674   *            true if currently within a multi-line comment
675   */
676  protected void setInsideMultiLineComment(boolean value) {
677    m_InsideMultiLineComment = value;
678  }
679
680  /**
681   * Parse the line for tokens to highlight.
682   *
683   * @param content
684   *            the content to parse
685   * @param startOffset
686   *            the start position
687   * @param endOffset
688   *            the end position
689   */
690  protected void checkForTokens(String content, int startOffset, int endOffset) {
691    while (startOffset <= endOffset) {
692      // skip the delimiters to find the start of a new token
693
694      while (isDelimiter(content.substring(startOffset, startOffset + 1))) {
695        if (startOffset < endOffset)
696          startOffset++;
697        else
698          return;
699      }
700
701      // Extract and process the entire token
702
703      if (isQuoteDelimiter(content.substring(startOffset, startOffset + 1)))
704        startOffset = getQuoteToken(content, startOffset, endOffset);
705      else
706        startOffset = getOtherToken(content, startOffset, endOffset);
707    }
708  }
709
710  /**
711   * Searches for a quote token.
712   *
713   * @param content
714   *            the content to search
715   * @param startOffset
716   *            the start of the search
717   * @param endOffset
718   *            the end of the search
719   * @return
720   *            the new position
721   */
722  protected int getQuoteToken(String content, int startOffset, int endOffset) {
723    String quoteDelimiter = content.substring(startOffset, startOffset + 1);
724    String escapeString = escapeQuote(quoteDelimiter);
725
726    int index;
727    int endOfQuote = startOffset;
728
729    // skip over the escape quotes in this quote
730
731    index = content.indexOf(escapeString, endOfQuote + 1);
732
733    while ((index > -1) && (index < endOffset)) {
734      endOfQuote = index + 1;
735      index = content.indexOf(escapeString, endOfQuote);
736    }
737
738    // now find the matching delimiter
739
740    index = content.indexOf(quoteDelimiter, endOfQuote + 1);
741
742    if ((index < 0) || (index > endOffset))
743      endOfQuote = endOffset;
744    else
745      endOfQuote = index;
746
747    m_Self.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1,
748        DEFAULT_STRING, false);
749
750    return endOfQuote + 1;
751  }
752
753  /**
754   * Searches for a keyword token.
755   *
756   * @param content
757   *            the content to search in
758   * @param startOffset
759   *            the position to start the search fromm
760   * @param endOffset
761   *            the position to end the search
762   * @return
763   *            the new position
764   */
765  protected int getOtherToken(String content, int startOffset, int endOffset) {
766    int endOfToken = startOffset + 1;
767
768    while (endOfToken <= endOffset) {
769      if (isDelimiter(content.substring(endOfToken, endOfToken + 1)))
770        break;
771      endOfToken++;
772    }
773
774    String token = content.substring(startOffset, endOfToken);
775
776    // see if this token has a highlighting format associated with it
777    MutableAttributeSet attr = getKeywordFormatting(token);
778    if (attr != null)
779      m_Self.setCharacterAttributes(startOffset, endOfToken - startOffset, attr, false);
780
781    return endOfToken + 1;
782  }
783
784  /**
785   * Assume the needle will the found at the start/end of the line.
786   *
787   * @param content
788   *            the content to search
789   * @param needle
790   *            the string to look for
791   * @param offset
792   *            the offset to start at
793   * @return
794   *            the index
795   */
796  protected int indexOf(String content, String needle, int offset) {
797    int index;
798
799    while ((index = content.indexOf(needle, offset)) != -1) {
800      String text = getLine(content, index).trim();
801
802      if (text.startsWith(needle) || text.endsWith(needle))
803        break;
804      else
805        offset = index + 1;
806    }
807
808    return index;
809  }
810
811  /**
812   * Assume the needle will the found at the start/end of the line.
813   *
814   * @param content
815   *            the content search
816   * @param needle
817   *            what to look for
818   * @param offset
819   *            the offset to start
820   * @return
821   *            the index
822   */
823  protected int lastIndexOf(String content, String needle, int offset) {
824    int index;
825
826    while ((index = content.lastIndexOf(needle, offset)) != -1) {
827      String text = getLine(content, index).trim();
828
829      if (text.startsWith(needle) || text.endsWith(needle))
830        break;
831      else
832        offset = index - 1;
833    }
834
835    return index;
836  }
837
838  /**
839   * Returns the line.
840   *
841   * @param content
842   *            the content
843   * @param offset
844   *            the offset to start at
845   * @return
846   *            the line
847   */
848  protected String getLine(String content, int offset) {
849    int line = m_RootElement.getElementIndex(offset);
850    Element lineElement = m_RootElement.getElement(line);
851    int start = lineElement.getStartOffset();
852    int end = lineElement.getEndOffset();
853    return content.substring(start, end - 1);
854  }
855
856  /**
857   * Checks whether the character is a delimiter.
858   *
859   * @param character
860   *            the character to check
861   * @return
862   *            true if a delimiter
863   */
864  public boolean isDelimiter(String character) {
865    return Character.isWhitespace(character.charAt(0)) || (m_Delimiters.indexOf(character.charAt(0)) > -1);
866  }
867
868  /**
869   * Checks whether the character is quote delimiter.
870   *
871   * @param character
872   *            the character to check
873   * @return
874   *            true if a quote delimiter
875   */
876  public boolean isQuoteDelimiter(String character) {
877    return (m_QuoteDelimiters.indexOf(character.charAt(0)) > -1);
878  }
879
880  /**
881   * Escapes the quote delimiter.
882   *
883   * @param quoteDelimiter
884   *            the string to escape
885   * @return
886   *            the escaped string
887   */
888  public String escapeQuote(String quoteDelimiter) {
889    return m_QuoteEscape + quoteDelimiter;
890  }
891
892  /**
893   * Adds the matching block end.
894   *
895   * @param offset
896   *            the offset
897   * @return
898   *            the string after adding the matching block end
899   * @throws BadLocationException
900   *            if the offset is invalid
901   */
902  protected String addMatchingBlockEnd(int offset) throws BadLocationException {
903    StringBuffer result;
904    StringBuffer whiteSpace = new StringBuffer();
905    int line = m_RootElement.getElementIndex(offset);
906    int i = m_RootElement.getElement(line).getStartOffset();
907
908    while (true) {
909      String temp = m_Self.getText(i, 1);
910
911      if (temp.equals(" ") || temp.equals("\t")) {
912        whiteSpace.append(temp);
913        i++;
914      }
915      else {
916        break;
917      }
918    }
919
920    // assemble string
921    result = new StringBuffer();
922    result.append(m_BlockStart);
923    result.append("\n");
924    result.append(whiteSpace.toString());
925    if (m_UseBlanks)
926      result.append(m_Indentation);
927    else
928      result.append("\t");
929    result.append("\n");
930    result.append(whiteSpace.toString());
931    result.append(m_BlockEnd);
932   
933    return result.toString();
934  }
935
936  /**
937   * gets the current font size.
938   *
939   * @return
940   *            the font size
941   */
942  public int getFontSize() {
943    return m_FontSize;
944  }
945
946  /**
947   * sets the current font size (affects all built-in styles).
948   *
949   * @param fontSize
950   *            the size
951   */
952  public void setFontSize(int fontSize) {
953    m_FontSize = fontSize;
954    StyleConstants.setFontSize(DEFAULT_NORMAL, fontSize);
955    StyleConstants.setFontSize(DEFAULT_STRING, fontSize);
956    StyleConstants.setFontSize(DEFAULT_COMMENT, fontSize);
957  }
958
959  /**
960   * gets the current font family.
961   *
962   * @return
963   *            the font name
964   */
965  public String getFontName() {
966    return m_FontName;
967  }
968
969  /**
970   * sets the current font family (affects all built-in styles).
971   *
972   * @param fontName
973   *            the font name
974   */
975  public void setFontName(String fontName) {
976    m_FontName = fontName;
977    StyleConstants.setFontFamily(DEFAULT_NORMAL, fontName);
978    StyleConstants.setFontFamily(DEFAULT_STRING, fontName);
979    StyleConstants.setFontFamily(DEFAULT_COMMENT, fontName);
980  }
981 
982  /**
983   * Sets the number of blanks to use for indentation.
984   *
985   * @param value       
986   *            the number of blanks
987   */
988  public void setIndentationSize(int value) {
989    int         i;
990   
991    m_Indentation = "";
992    for (i = 0; i < value; i++)
993      m_Indentation += " ";
994  }
995 
996  /**
997   * Returns the number of blanks used for indentation.
998   *
999   * @return           
1000   *            the number of blanks
1001   */
1002  public int getIndentationSize() {
1003    return m_Indentation.length();
1004  }
1005 
1006  /**
1007   * Sets the delimiter characters to use.
1008   *
1009   * @param value       
1010   *            the characters
1011   */
1012  public void setDelimiters(String value) {
1013    m_Delimiters = value;
1014  }
1015 
1016  /**
1017   * Returns the delimiter characters to use.
1018   *
1019   * @return           
1020   *            the characters
1021   */
1022  public String getDelimiters() {
1023    return m_Delimiters;
1024  }
1025 
1026  /**
1027   * Sets the quote delimiter characters to use.
1028   *
1029   * @param value       
1030   *            the characters
1031   */
1032  public void setQuoteDelimiters(String value) {
1033    m_QuoteDelimiters = value;
1034  }
1035 
1036  /**
1037   * Returns the quote delimiter characters to use.
1038   *
1039   * @return           
1040   *            the characters
1041   */
1042  public String getQuoteDelimiters() {
1043    return m_QuoteDelimiters;
1044  }
1045 
1046  /**
1047   * Sets the character to use for escaping a quote character.
1048   *
1049   * @param value       
1050   *            the character
1051   */
1052  public void setQuoteEscape(String value) {
1053    m_QuoteEscape = value;
1054  }
1055 
1056  /**
1057   * Returns the character for escaping a quote delimiter.
1058   *
1059   * @return           
1060   *            the character
1061   */
1062  public String getQuoteEscape() {
1063    return m_QuoteEscape;
1064  }
1065 
1066  /**
1067   * Sets the string that is the start of a single-line comment.
1068   *
1069   * @param value       
1070   *            the string
1071   */
1072  public void setSingleLineCommentStart(String value) {
1073    m_SingleLineCommentStart = value;
1074  }
1075
1076  /**
1077   * Retrusn the single line comment start string.
1078   *
1079   * @return
1080   *            the start string
1081   */
1082  public String getSingleLineCommentStart() {
1083    return m_SingleLineCommentStart;
1084  }
1085 
1086  /**
1087   * Sets the string that is the start of a multi-line comment.
1088   *
1089   * @param value       
1090   *            the string
1091   */
1092  public void setMultiLineCommentStart(String value) {
1093    m_MultiLineCommentStart = value;
1094  }
1095 
1096  /**
1097   * Returns the string that is the start of a multi-line comment.
1098   *
1099   * @return           
1100   *            the string
1101   */
1102  public String getMultiLineCommentStart() {
1103    return m_MultiLineCommentStart;
1104  }
1105 
1106  /**
1107   * Sets the string that is the end of a multi-line comment.
1108   *
1109   * @param value       the string
1110   */
1111  public void setMultiLineCommentEnd(String value) {
1112    m_MultiLineCommentEnd = value;
1113  }
1114
1115  /**
1116   * Returns the end of a multi-line comment.
1117   *
1118   * @return
1119   *            the end string
1120   */
1121  public String getMultiLineCommentEnd() {
1122    return m_MultiLineCommentEnd;
1123  }
1124 
1125  /**
1126   * Sets the string that is the start of a block.
1127   *
1128   * @param value
1129   *            the string
1130   */
1131  public void setBlockStart(String value) {
1132    m_BlockStart = value;
1133  }
1134
1135  /**
1136   * Returns the start of a block.
1137   *
1138   * @return
1139   *            the end string
1140   */
1141  public String getBlockStart() {
1142    return m_BlockStart;
1143  }
1144 
1145  /**
1146   * Sets the string that is the end of a block.
1147   *
1148   * @param value       
1149   *            the string
1150   */
1151  public void setBlockEnd(String value) {
1152    m_BlockEnd = value;
1153  }
1154
1155  /**
1156   * Returns the end of a block.
1157   *
1158   * @return
1159   *            the end string
1160   */
1161  public String getBlockEnd() {
1162    return m_BlockEnd;
1163  }
1164 
1165  /**
1166   * Sets whether matching block ends are inserted or not.
1167   *
1168   * @param value       
1169   *            if true then matching block ends are inserted
1170   */
1171  public void setAddMatchingEndBlocks(boolean value) {
1172    m_AddMatchingEndBlocks = value;
1173  }
1174
1175  /**
1176   * Returns whether matching block ends are inserted or not.
1177   *
1178   * @return
1179   *            true if matching block ends are inserted
1180   */
1181  public boolean getAddMatchingEndBlocks() {
1182    return m_AddMatchingEndBlocks;
1183  }
1184 
1185  /**
1186   * Sets whether to use blanks instead of tabs.
1187   *
1188   * @param value       
1189   *            if true then blanks are used instead of tabs
1190   */
1191  public void setUseBlanks(boolean value) {
1192    m_UseBlanks = value;
1193  }
1194
1195  /**
1196   * Returns whether blanks are used instead of tabs.
1197   *
1198   * @return
1199   *            true if blanks are used instead of tabs
1200   */
1201  public boolean getUseBlanks() {
1202    return m_UseBlanks;
1203  }
1204 
1205  /**
1206   * Sets the background color.
1207   *
1208   * @param value       
1209   *            the background color
1210   */
1211  public void setBackgroundColor(Color value) {
1212    m_BackgroundColor = value;
1213  }
1214 
1215  /**
1216   * Returns the background color.
1217   *
1218   * @return
1219   *            the background color
1220   */
1221  public Color getBackgroundColor() {
1222    return m_BackgroundColor;
1223  }
1224 
1225  /**
1226   * Sets whether to enable multi-line comments.
1227   *
1228   * @param value       
1229   *            if true then multi-line comments are enabled
1230   */
1231  public void setMultiLineComment(boolean value) {
1232    m_MultiLineComment = value;
1233  }
1234
1235  /**
1236   * Returns whether multi-line comments are enabled.
1237   *
1238   * @return
1239   *            true if multi-line comments are enabled
1240   */
1241  public boolean getMultiLineComment() {
1242    return m_MultiLineComment;
1243  }
1244 
1245  /**
1246   * Sets whether the keywords are case-sensitive or not.
1247   *
1248   * @param value       
1249   *            if true then keywords are treated case-sensitive
1250   */
1251  public void setCaseSensitive(boolean value) {
1252    m_CaseSensitive = value;
1253  }
1254
1255  /**
1256   * Returns whether blanks are used instead of tabs.
1257   *
1258   * @return
1259   *            true if keywords are case-sensitive
1260   */
1261  public boolean getCaseSensitive() {
1262    return m_CaseSensitive;
1263  }
1264}
Note: See TracBrowser for help on using the repository browser.