001: /*
002: * Copyright (c) 2001, Jacob Smullyan.
003: *
004: * This is part of SkunkDAV, a WebDAV client. See http://skunkdav.sourceforge.net/
005: * for the latest version.
006: *
007: * SkunkDAV is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License as published
009: * by the Free Software Foundation; either version 2, or (at your option)
010: * any later version.
011: *
012: * SkunkDAV is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with SkunkDAV; see the file COPYING. If not, write to the Free
019: * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
020: * 02111-1307, USA.
021: */
022:
023: package org.skunk.swing.text.syntax;
024:
025: import java.awt.Color;
026: import java.awt.Font;
027: import java.io.BufferedReader;
028: import java.io.BufferedWriter;
029: import java.io.InputStream;
030: import java.io.InputStreamReader;
031: import java.io.IOException;
032: import java.io.Serializable;
033: import java.io.StringReader;
034: import java.io.StringWriter;
035: import java.lang.reflect.Field;
036: import java.util.HashMap;
037: import java.util.Iterator;
038: import java.util.NoSuchElementException;
039: import java.util.StringTokenizer;
040: import java.util.TreeSet;
041: import org.skunk.trace.Debug;
042:
043: public class SyntaxStyle implements Serializable, Cloneable {
044: static final long serialVersionUID = 6891694368647729611L;
045:
046: public static final String DEFAULT_STYLE = "default";
047: public static final String UNFINISHED_STYLE = "unfinished";
048: public static final String KEYWORD1_STYLE = "keyword1";
049: public static final String KEYWORD2_STYLE = "keyword2";
050: public static final String KEYWORD3_STYLE = "keyword3";
051: public static final String KEYWORD4_STYLE = "keyword4";
052: public static final String LITERAL1_STYLE = "literal1";
053: public static final String LITERAL2_STYLE = "literal2";
054: public static final String LITERAL3_STYLE = "literal3";
055: public static final String LITERAL4_STYLE = "literal4";
056: public static final String COMMENT1_STYLE = "comment1";
057: public static final String COMMENT2_STYLE = "comment2";
058: public static final String COMMENT3_STYLE = "comment3";
059: public static final String COMMENT4_STYLE = "comment4";
060: public static final String TAG1_STYLE = "tag1";
061: public static final String TAG2_STYLE = "tag2";
062: public static final String TAG3_STYLE = "tag3";
063: public static final String TAG4_STYLE = "tag4";
064: public static final String OPERATOR_STYLE = "operator";
065: public static final String SEPARATOR1_STYLE = "separator1";
066: public static final String SEPARATOR2_STYLE = "separator2";
067: public static final String INCLUDE_STYLE = "include";
068: public static final String IDENTIFIER1_STYLE = "identifier1";
069: public static final String IDENTIFIER2_STYLE = "identifier2";
070: public static final String ERROR_STYLE = "error";
071:
072: public static final int UNFINISHED = 1;
073: public static final int DEFAULT = 2;
074: public static final int KEYWORD1 = 3;
075: public static final int KEYWORD2 = 4;
076: public static final int KEYWORD3 = 5;
077: public static final int KEYWORD4 = 6;
078: public static final int LITERAL1 = 7;
079: public static final int LITERAL2 = 8;
080: public static final int LITERAL3 = 9;
081: public static final int LITERAL4 = 10;
082: public static final int COMMENT1 = 11;
083: public static final int COMMENT2 = 12;
084: public static final int COMMENT3 = 13;
085: public static final int COMMENT4 = 14;
086: public static final int TAG1 = 15;
087: public static final int TAG2 = 16;
088: public static final int TAG3 = 17;
089: public static final int TAG4 = 18;
090: public static final int OPERATOR = 19;
091: public static final int SEPARATOR1 = 20;
092: public static final int SEPARATOR2 = 21;
093: public static final int INCLUDE = 22;
094: public static final int IDENTIFIER1 = 23;
095: public static final int IDENTIFIER2 = 24;
096: public static final int ERROR = 25;
097:
098: public static final Font DEFAULT_FONT = new Font("Monospaced",
099: Font.PLAIN, 12);
100: public static final Color DEFAULT_FOREGROUND = Color.white;
101: /* abandoning 0, 51, 51 for a new mod look! */
102: public static final Color DEFAULT_BACKGROUND = new Color(0, 51, 102);
103:
104: public static final String DEFAULT_STYLES_FILE = "default.styles";
105:
106: private static SyntaxStyleSet defaultStyleSet = new SyntaxStyleSet();
107:
108: private SyntaxStyleSet parentSet;
109: private Color foregroundColor;
110: private boolean bold;
111: private boolean italic;
112: private String name;
113: private int styleID;
114: private Font font;
115:
116: static {
117: initDefaultStyles(defaultStyleSet);
118: }
119:
120: public static final SyntaxStyleSet getDefaultStyleSet() {
121: return SyntaxStyle.defaultStyleSet;
122: }
123:
124: public static final Iterator styles() {
125: return defaultStyleSet.styles();
126: }
127:
128: public static final SyntaxStyle getStyle(String name) {
129: return defaultStyleSet.getStyle(name);
130: }
131:
132: public static final SyntaxStyle getStyle(int id) {
133: return defaultStyleSet.getStyle(id);
134: }
135:
136: public static final Font getDefaultFont() {
137: return defaultStyleSet.getDefaultFont();
138: }
139:
140: public static final void initDefaultStyles(SyntaxStyleSet styleSet) {
141: try {
142: InputStream in = SyntaxStyle.class
143: .getResourceAsStream(DEFAULT_STYLES_FILE);
144: BufferedReader breader = new BufferedReader(
145: new InputStreamReader(in));
146: styleSet.readConfig(breader);
147: } catch (Exception oyVeh) {
148: if (Debug.DEBUG)
149: Debug.trace(SyntaxStyle.class, Debug.DP2, oyVeh);
150: }
151: }
152:
153: private static Color getColor(String colorStr)
154: throws RuntimeException {
155: if (colorStr.indexOf(",") >= 0) {
156: StringTokenizer st = new StringTokenizer(colorStr, ",");
157: try {
158: int r = Integer.parseInt(st.nextToken());
159: int g = Integer.parseInt(st.nextToken());
160: int b = Integer.parseInt(st.nextToken());
161: return new Color(r, g, b);
162: } catch (NoSuchElementException smullinium) {
163: if (Debug.DEBUG)
164: Debug.trace(SyntaxStyle.class, Debug.DP2,
165: smullinium);
166: throw new RuntimeException(smullinium.getMessage());
167: } catch (NumberFormatException numbForm) {
168: if (Debug.DEBUG)
169: Debug.trace(SyntaxStyle.class, Debug.DP2, numbForm);
170: throw new RuntimeException(numbForm.getMessage());
171: }
172: } else {
173: //a string representing one of the static fields in the Color class. instantiate it via reflection
174: try {
175: Field f = Color.class.getField(colorStr);
176: return (Color) f.get(null);
177: } catch (Exception e) {
178: if (Debug.DEBUG)
179: Debug.trace(SyntaxStyle.class, Debug.DP2, e);
180: throw new RuntimeException(e.getMessage());
181: }
182: }
183: }
184:
185: private static final int getStyle(boolean bBold, boolean bItalic) {
186: return (bBold) ? Font.BOLD + ((bItalic) ? Font.ITALIC : 0)
187: : (bItalic) ? Font.ITALIC : Font.PLAIN;
188: }
189:
190: public static final Font deriveFont(Font baseFont, boolean bold,
191: boolean italic) {
192: return baseFont.deriveFont(getStyle(bold, italic));
193: }
194:
195: private SyntaxStyle(String name, Color foregroundColor,
196: boolean bold, boolean italic, int styleID,
197: SyntaxStyleSet sss) {
198: this .name = name;
199: this .foregroundColor = foregroundColor;
200: this .bold = bold;
201: this .italic = italic;
202: this .styleID = styleID;
203: if (sss != null)
204: sss.addStyle(this );
205: this .font = deriveFont(bold, italic);
206: }
207:
208: public Color getForegroundColor() {
209: return foregroundColor;
210: }
211:
212: public void setForegroundColor(Color foregroundColor) {
213: this .foregroundColor = foregroundColor;
214: }
215:
216: private Font deriveFont(boolean bBold, boolean bItalic) {
217: int fontStyle = getStyle(bBold, bItalic);
218: Font defaultFont = (parentSet == null) ? getDefaultFont()
219: : parentSet.getDefaultFont();
220: return defaultFont.deriveFont(fontStyle);
221: }
222:
223: public void setBold(boolean bold) {
224: this .bold = bold;
225: this .font = deriveFont(bold, italic);
226: }
227:
228: public boolean isBold() {
229: return this .bold;
230: }
231:
232: public void setItalic(boolean italic) {
233: this .italic = italic;
234: this .font = deriveFont(bold, italic);
235: }
236:
237: public boolean isItalic() {
238: return this .italic;
239: }
240:
241: public Font getFont() {
242: return font;
243: }
244:
245: private void setFont(Font font) {
246: this .font = font;
247: }
248:
249: public final String getName() {
250: return this .name;
251: }
252:
253: public final int getID() {
254: return this .styleID;
255: }
256:
257: public String toString() {
258: return new StringBuffer("SyntaxStyle [name=").append(getName())
259: .append(", id=").append(getID()).append(", font=")
260: .append(getFont()).append(", bold=").append(isBold())
261: .append(", italic=").append(isItalic()).append(
262: ", color=").append(getForegroundColor())
263: .append("]").toString();
264: }
265:
266: /**
267: * placing the styleNameMap in this container
268: * eases its configuration by the config package.
269: */
270: public static class SyntaxStyleSet implements Serializable,
271: Cloneable {
272: static final long serialVersionUID = -5571840012443245785L;
273:
274: public static final String DEFAULT_FONT_PROPERTY = "defaultFont";
275: public static final String STYLE_NAME_MAP_PROPERTY = "styleNameMap";
276: public static final String CONFIG_DATA_PROPERTY = "configData";
277:
278: private HashMap styleNameMap;
279: private HashMap styleIDMap;
280: private Font defaultFont;
281:
282: public SyntaxStyleSet() {
283: this (SyntaxStyle.DEFAULT_FONT);
284: }
285:
286: public SyntaxStyleSet(Font defaultFont) {
287: this .styleNameMap = new HashMap();
288: this .styleIDMap = new HashMap();
289: this .defaultFont = defaultFont;
290: }
291:
292: private void addStyle(SyntaxStyle ss) {
293: styleNameMap.put(ss.getName(), ss);
294: styleIDMap.put(new Integer(ss.getID()), ss);
295: ss.parentSet = this ;
296: }
297:
298: public HashMap getStyleNameMap() {
299: return this .styleNameMap;
300: }
301:
302: protected final void clear() {
303: styleNameMap.clear();
304: styleIDMap.clear();
305: }
306:
307: public final Iterator styles() {
308: return styleNameMap.values().iterator();
309: }
310:
311: public final void setStyleNameMap(HashMap styleNameMap) {
312: this .styleNameMap = styleNameMap;
313: styleIDMap.clear();
314: for (Iterator it = styles(); it.hasNext();) {
315: SyntaxStyle ss = (SyntaxStyle) it.next();
316: styleIDMap.put(new Integer(ss.getID()), ss);
317: }
318: }
319:
320: public final SyntaxStyle getStyle(String name) {
321: if (styleNameMap.containsKey(name))
322: return (SyntaxStyle) styleNameMap.get(name);
323: return null;
324: }
325:
326: public final SyntaxStyle getStyle(int id) {
327: Integer idInt = new Integer(id);
328: if (styleIDMap.containsKey(idInt))
329: return (SyntaxStyle) styleIDMap.get(idInt);
330: return null;
331: }
332:
333: public final String getConfigData() {
334: StringWriter sw = new StringWriter();
335: try {
336: writeConfig(new BufferedWriter(sw));
337: } catch (IOException oyVeh) {
338: return null;
339: }
340: return sw.getBuffer().toString();
341: }
342:
343: public final void setConfigData(String configData,
344: boolean clearOut) {
345: if (clearOut)
346: clear();
347: setConfigData(configData);
348: }
349:
350: public final void setConfigData(String configData) {
351: StringReader sr = new StringReader(configData);
352: try {
353: readConfig(new BufferedReader(sr));
354: } catch (IOException oyVeh) {
355: //will already be logged
356: }
357: }
358:
359: public Font getDefaultFont() {
360: return this .defaultFont;
361: }
362:
363: public void setDefaultFont(Font f) {
364: this .defaultFont = f;
365: for (Iterator it = styles(); it.hasNext();) {
366: SyntaxStyle ss = (SyntaxStyle) it.next();
367: ss.setFont(SyntaxStyle.deriveFont(f, ss.isBold(), ss
368: .isItalic()));
369: }
370: }
371:
372: private static final String CONFIG_BOILERPLATE = "####################################################################"
373: + "\n"
374: + "# NAME COLOR BOLD ITALIC NO #"
375: + "\n"
376: + "####################################################################"
377: + "\n";
378:
379: private static final int CONFIG_TAB_SIZE = 16;
380:
381: /**
382: * writes the state of the style set in a config file format to the stream.
383: * @param brighter the stream to which to write
384: */
385: public void writeConfig(BufferedWriter brighter)
386: throws IOException {
387: StringBuffer sb = new StringBuffer(CONFIG_BOILERPLATE);
388: for (Iterator it = (new TreeSet(styleIDMap.keySet()))
389: .iterator(); it.hasNext();) {
390: SyntaxStyle ss = (SyntaxStyle) styleIDMap
391: .get(it.next());
392: sb.append(pad(ss.getName(), CONFIG_TAB_SIZE));
393: formatColor(sb, ss.getForegroundColor());
394: sb.append(pad(new Boolean(ss.isBold()).toString(),
395: CONFIG_TAB_SIZE));
396: sb.append(pad(new Boolean(ss.isItalic()).toString(),
397: CONFIG_TAB_SIZE));
398: sb.append(ss.getID());
399: sb.append("\n");
400: }
401: try {
402: brighter.write(sb.toString(), 0, sb.length());
403: brighter.flush();
404: brighter.close();
405: } catch (IOException oyVeh) {
406: if (Debug.DEBUG)
407: Debug.trace(this , Debug.DP2, oyVeh);
408: throw oyVeh;
409: }
410: }
411:
412: private void formatColor(StringBuffer sb, Color c) {
413: int startlen = sb.length();
414: sb.append(c.getRed());
415: sb.append(",");
416: sb.append(c.getGreen());
417: sb.append(",");
418: sb.append(c.getBlue());
419: int padding = Math.max(0, CONFIG_TAB_SIZE
420: - (sb.length() - startlen));
421: for (int i = 0; i < padding; i++)
422: sb.append(" ");
423: }
424:
425: private String pad(String s, int length) {
426: StringBuffer sb = new StringBuffer(s);
427: while (sb.length() < length)
428: sb.append(" ");
429: return sb.toString();
430: }
431:
432: /**
433: * sets the style set according the config information in the stream,
434: * in the same format generated by writeConfig (and in "default.styles", the
435: * default config file).
436: * @param breader the stream from which to read the config information.
437: */
438: public void readConfig(BufferedReader breader)
439: throws IOException {
440: try {
441: String line;
442: while ((line = breader.readLine()) != null) {
443: if (line.trim().startsWith("#"))
444: continue;
445: StringTokenizer st = new StringTokenizer(line);
446: String name = st.nextToken();
447: Color color = SyntaxStyle.getColor(st.nextToken());
448: boolean bBold = new Boolean(st.nextToken())
449: .booleanValue();
450: boolean bItalic = new Boolean(st.nextToken())
451: .booleanValue();
452: int number = Integer.parseInt(st.nextToken());
453: new SyntaxStyle(name, color, bBold, bItalic,
454: number, this );
455: }
456: } catch (Exception oyVeh) {
457: if (Debug.DEBUG)
458: Debug.trace(SyntaxStyleSet.class, Debug.DP2, oyVeh);
459: throw new IOException(oyVeh.getMessage());
460: }
461: }
462: }
463: }
464:
465: /* $Log: SyntaxStyle.java,v $
466: /* Revision 1.9 2001/02/16 18:15:11 smulloni
467: /* many fixes to TextEditorCustomizer. FileMode and SyntaxStyle now have a
468: /* configData property (they will probably be made into sibling classes).
469: /*
470: /* Revision 1.8 2001/02/15 06:39:05 smulloni
471: /* changed default color scheme, with useful input from Anne Marie Hantho
472: /*
473: /* Revision 1.7 2001/02/13 22:53:50 smulloni
474: /* adding a syntax highlighting mode for STML (Skunk Template Markup Language);
475: /* fixed a bug in SyntaxStyle in reading default.styles.
476: /*
477: /* Revision 1.6 2001/02/08 23:48:50 smulloni
478: /* now load default styles from a config file. Styles have been renamed to be
479: /* more general (comment3 instead of docComment) so future modes won't need to
480: /* invent more styles.
481: /*
482: /* Revision 1.5 2001/02/06 22:13:41 smulloni
483: /* first more-or-less working version of syntax highlighting, with customization.
484: /*
485: /* Revision 1.4 2001/02/06 00:11:18 smulloni
486: /* struggle, perhaps futile, with the TextEditorCustomizer and other implicated
487: /* classes
488: /*
489: /* Revision 1.3 2001/02/02 23:30:33 smulloni
490: /* adding customization features to the text editor.
491: /*
492: /* Revision 1.2 2001/01/30 23:03:19 smulloni
493: /* beginning of integration of syntax highlighting into SimpleTextEditor.
494: /*
495: /* Revision 1.1 2001/01/29 22:30:20 smulloni
496: /* support for custom views for syntax highlighting.
497: /* */
|