Changes:
1. Refractor the structure, isolate those reuseable/generic part out of the SyntaxHighlighter
diff --git a/src/prettify/PrettifyParser.java b/src/prettify/PrettifyParser.java
new file mode 100644
index 0000000..041189a
--- /dev/null
+++ b/src/prettify/PrettifyParser.java
@@ -0,0 +1,41 @@
+package prettify;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import prettify.parser.Job;
+import prettify.parser.Prettify;
+import syntaxhighlight.ParseResult;
+import syntaxhighlight.Parser;
+
+/**
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class PrettifyParser implements Parser {
+
+  protected Prettify prettify;
+
+  public PrettifyParser() {
+    prettify = new Prettify();
+  }
+
+  @Override
+  public List<ParseResult> parse(String fileExtension, String content) {
+    Job job = new Job(0, content);
+    prettify.langHandlerForExtension(fileExtension, content).decorate(job);
+    List<Object> decorations = job.getDecorations();
+
+
+    List<ParseResult> returnList = new ArrayList<ParseResult>();
+
+    Integer startPos = 0, endPos = 0;
+    // apply style according to the style list
+    for (int i = 0, iEnd = decorations.size(); i < iEnd; i += 2) {
+      endPos = i + 2 < iEnd ? (Integer) decorations.get(i + 2) : content.length();
+      startPos = (Integer) decorations.get(i);
+      returnList.add(new ParseResult(startPos, endPos - startPos, Arrays.asList(new String[]{(String) decorations.get(i + 1)})));
+    }
+
+    return returnList;
+  }
+}
diff --git a/src/prettify/example/Example.java b/src/prettify/example/Example.java
index 366f217..33f4df3 100644
--- a/src/prettify/example/Example.java
+++ b/src/prettify/example/Example.java
@@ -22,8 +22,10 @@
 import javax.swing.JFrame;
 import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
-import prettify.SyntaxHighlighter;
+import prettify.PrettifyParser;
 import prettify.theme.ThemeDefault;
+import syntaxhighlight.Parser;
+import syntaxhighlight.SyntaxHighlighter;
 
 /**
  * Usage example. This will just cover some of the functions. To know other 
@@ -88,8 +90,10 @@
         long start, end;
         start = System.currentTimeMillis();
 
+        // initialize the parser
+        Parser parser = new PrettifyParser();
         // use Default theme
-        SyntaxHighlighter highlighter = new SyntaxHighlighter(new ThemeDefault());
+        SyntaxHighlighter highlighter = new SyntaxHighlighter(parser, new ThemeDefault());
         // set the line number count from 10 instead of 1
         highlighter.setFirstLine(10);
         // set to highlight line 13, 27, 28, 38, 42, 43 and 53
diff --git a/src/prettify/gui/package-info.java b/src/prettify/gui/package-info.java
deleted file mode 100644
index cdbe674..0000000
--- a/src/prettify/gui/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**

- * GUI.

- */

-package prettify.gui;
\ No newline at end of file
diff --git a/src/prettify/theme/Theme.java b/src/prettify/theme/Theme.java
deleted file mode 100644
index fc34eda..0000000
--- a/src/prettify/theme/Theme.java
+++ /dev/null
@@ -1,676 +0,0 @@
-// Copyright (C) 2011 Chan Wai Shing
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify.theme;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import prettify.gui.JTextComponentRowHeader;
-
-/**
- * Theme for the Java Prettify.
- * <p>To make a new theme, either extending this class of initiate this class 
- * and set the parameter by the setter. For the default value, please refer to 
- * the constructor.</p>
- * @author Chan Wai Shing <cws1989@gmail.com>
- */
-public class Theme {
-
-  /**
-   * Indicate whether it is in debug mode or not.
-   */
-  protected final static boolean debug;
-
-  static {
-    String debugMode = System.getProperty("PrettifyDebugMode");
-    debug = debugMode == null || !debugMode.equals("true") ? false : true;
-  }
-  /**
-   * The font of the script text.
-   */
-  protected Font font;
-  /**
-   * The background color of the script text area.
-   */
-  protected Color background;
-  /**
-   * The background color of the highlighted line of script text.
-   */
-  protected Color highlightedBackground;
-  /**
-   * Gutter (line number column on the left)
-   */
-  /**
-   * The color of the gutter text.
-   */
-  protected Color gutterText;
-  /**
-   * The color of the border that joint the gutter and the script text panel.
-   */
-  protected Color gutterBorderColor;
-  /**
-   * The width of the border that joint the gutter and the script text panel.
-   */
-  protected int gutterBorderWidth;
-  /**
-   * The font of the gutter text.
-   */
-  protected Font gutterTextFont;
-  /**
-   * The minimum padding from 'the leftmost of the line number text' to 
-   * 'the left margin'.
-   */
-  protected int gutterTextPaddingLeft;
-  /**
-   * The minimum padding from 'the rightmost of the line number text' to 
-   * 'the right margin' (not to the gutter border).
-   */
-  protected int gutterTextPaddingRight;
-  protected Style string;
-  protected Style keyword;
-  protected Style comment;
-  protected Style type;
-  protected Style literal;
-  protected Style punctuation;
-  protected Style plain;
-  protected Style tag;
-  protected Style declaration;
-  protected Style source;
-  protected Style attributeName;
-  protected Style attributeValue;
-  protected Style noCode;
-  protected Style openBracket;
-  protected Style closeBracket;
-  protected Style variable;
-  protected Style function;
-
-  /**
-   * Constructor.<br />
-   * <p>
-   * <b>Default value:</b><br />
-   * <ul>
-   * <li>font: Consolas 12pt</li>
-   * <li>background: white</li>
-   * <li>gutter text: black</li>
-   * <li>gutter border: R: 184, G: 184, B: 184</li>
-   * <li>gutter border width: 3px</li>
-   * <li>gutter text font: Consolas 12pt</li>
-   * <li>gutter text padding-left: 7px</li>
-   * <li>gutter text padding-right: 7px</li>
-   * <li>plain, comments, string, keyword, preprocessor, variable, value, 
-   * functions, constants, script, scriptBackground, color1, color2, color3: 
-   * no style set</li>
-   * </ul>
-   * </p>
-   */
-  public Theme() {
-    font = new Font("Consolas", Font.PLAIN, 12);
-    background = Color.white;
-
-    highlightedBackground = Color.gray;
-
-    gutterText = Color.black;
-    gutterBorderColor = new Color(184, 184, 184);
-    gutterBorderWidth = 3;
-    gutterTextFont = new Font("Consolas", Font.PLAIN, 12);
-    gutterTextPaddingLeft = 7;
-    gutterTextPaddingRight = 7;
-
-    string = new Style();
-    keyword = new Style();
-    comment = new Style();
-    type = new Style();
-    literal = new Style();
-    punctuation = new Style();
-    plain = new Style();
-    tag = new Style();
-    declaration = new Style();
-    source = new Style();
-    attributeName = new Style();
-    attributeValue = new Style();
-    noCode = new Style();
-    openBracket = new Style();
-    closeBracket = new Style();
-    variable = new Style();
-    function = new Style();
-  }
-
-  /**
-   * Apply the theme to the row header panel.
-   * @param rowHeader the row header to apply theme on
-   */
-  public void setTheme(JTextComponentRowHeader rowHeader) {
-    rowHeader.setBackground(background);
-    rowHeader.setHighlightedColor(background);
-
-    rowHeader.setForeground(gutterText);
-    rowHeader.setBorderColor(gutterBorderColor);
-    rowHeader.setBorderWidth(gutterBorderWidth);
-    rowHeader.setFont(gutterTextFont);
-    rowHeader.setPaddingLeft(gutterTextPaddingLeft);
-    rowHeader.setPaddingRight(gutterTextPaddingRight);
-  }
-
-  /**
-   * Get the {@link prettify.theme.Style} by keyword.
-   * @param key the keyword, possible values: plain, comments, string, keyword, 
-   * preprocessor, variable, value, functions, constants, script, 
-   * scriptBackground, color1, color2 or color3
-   * @return the {@link prettify.theme.Style} related to the {@code key}; if 
-   * the style related to the <code>key</code> not exist, the style of 'plain'
-   * will return.
-   */
-  public Style getStyle(String key) {
-    if (key == null) {
-      return plain;
-    }
-    if (key.equals("str")) {
-      return string;
-    } else if (key.equals("kwd")) {
-      return keyword;
-    } else if (key.equals("com")) {
-      return comment;
-    } else if (key.equals("typ")) {
-      return type;
-    } else if (key.equals("lit")) {
-      return literal;
-    } else if (key.equals("pun")) {
-      return punctuation;
-    } else if (key.equals("pln")) {
-      return plain;
-    } else if (key.equals("tag")) {
-      return tag;
-    } else if (key.equals("dec")) {
-      return declaration;
-    } else if (key.equals("src")) {
-      return source;
-    } else if (key.equals("atn")) {
-      return attributeName;
-    } else if (key.equals("atv")) {
-      return attributeValue;
-    } else if (key.equals("nocode")) {
-      return noCode;
-    } else if (key.equals("opn")) {
-      return openBracket;
-    } else if (key.equals("clo")) {
-      return closeBracket;
-    } else if (key.equals("var")) {
-      return variable;
-    } else if (key.equals("fun")) {
-      return function;
-    } else {
-      // key not exist
-      return plain;
-    }
-  }
-
-  /**
-   * The font of the script text.
-   * @return the font
-   */
-  public Font getFont() {
-    return font;
-  }
-
-  /**
-   * The font of the script text.
-   * @param font the font
-   */
-  public void setFont(Font font) {
-    if (font == null) {
-      throw new NullPointerException("argument 'font' cannot be null");
-    }
-    this.font = font;
-  }
-
-  /**
-   * The background color of the script text area.
-   * @return the color
-   */
-  public Color getBackground() {
-    return background;
-  }
-
-  /**
-   * The background color of the script text area.
-   * @param background the color
-   */
-  public void setBackground(Color background) {
-    if (background == null) {
-      throw new NullPointerException("argument 'background' cannot be null");
-    }
-    this.background = background;
-  }
-
-  /**
-   * The background color of the highlighted line of script text.
-   * @return the color
-   */
-  public Color getHighlightedBackground() {
-    return highlightedBackground;
-  }
-
-  /**
-   * The background color of the highlighted line of script text.
-   * @param highlightedBackground the color
-   */
-  public void setHighlightedBackground(Color highlightedBackground) {
-    if (highlightedBackground == null) {
-      throw new NullPointerException("argument 'highlightedBackground' cannot be null");
-    }
-    this.highlightedBackground = highlightedBackground;
-  }
-
-  /**
-   * The color of the gutter text.
-   * @return the color
-   */
-  public Color getGutterText() {
-    return gutterText;
-  }
-
-  /**
-   * The color of the gutter text.
-   * @param gutterText the color
-   */
-  public void setGutterText(Color gutterText) {
-    if (gutterText == null) {
-      throw new NullPointerException("argument 'gutterText' cannot be null");
-    }
-    this.gutterText = gutterText;
-  }
-
-  /**
-   * The color of the border that joint the gutter and the script text panel.
-   * @return the color
-   */
-  public Color getGutterBorderColor() {
-    return gutterBorderColor;
-  }
-
-  /**
-   * The color of the border that joint the gutter and the script text panel.
-   * @param gutterBorderColor the color
-   */
-  public void setGutterBorderColor(Color gutterBorderColor) {
-    if (gutterBorderColor == null) {
-      throw new NullPointerException("argument 'gutterBorderColor' cannot be null");
-    }
-    this.gutterBorderColor = gutterBorderColor;
-  }
-
-  /**
-   * The width of the border that joint the gutter and the script text panel.
-   * @return the width in pixel
-   */
-  public int getGutterBorderWidth() {
-    return gutterBorderWidth;
-  }
-
-  /**
-   * The width of the border that joint the gutter and the script text panel.
-   * @param gutterBorderWidth in pixel
-   */
-  public void setGutterBorderWidth(int gutterBorderWidth) {
-    this.gutterBorderWidth = gutterBorderWidth;
-  }
-
-  /**
-   * The font of the gutter text.
-   * @return the font
-   */
-  public Font getGutterTextFont() {
-    return gutterTextFont;
-  }
-
-  /**
-   * The font of the gutter text.
-   * @param gutterTextFont the font
-   */
-  public void setGutterTextFont(Font gutterTextFont) {
-    if (gutterTextFont == null) {
-      throw new NullPointerException("argument 'gutterTextFont' cannot be null");
-    }
-    this.gutterTextFont = gutterTextFont;
-  }
-
-  /**
-   * The minimum padding from 'the leftmost of the line number text' to 'the left margin'.
-   * @return the padding in pixel
-   */
-  public int getGutterTextPaddingLeft() {
-    return gutterTextPaddingLeft;
-  }
-
-  /**
-   * The minimum padding from 'the leftmost of the line number text' to 
-   * 'the left margin'.
-   * @param gutterTextPaddingLeft in pixel
-   */
-  public void setGutterTextPaddingLeft(int gutterTextPaddingLeft) {
-    this.gutterTextPaddingLeft = gutterTextPaddingLeft;
-  }
-
-  /**
-   * The minimum padding from 'the rightmost of the line number text' to 
-   * 'the right margin' (not to the gutter border).
-   * @return the padding in pixel
-   */
-  public int getGutterTextPaddingRight() {
-    return gutterTextPaddingRight;
-  }
-
-  /**
-   * The minimum padding from 'the rightmost of the line number text' to 
-   * 'the right margin' (not to the gutter border).
-   * @param gutterTextPaddingRight in pixel
-   */
-  public void setGutterTextPaddingRight(int gutterTextPaddingRight) {
-    this.gutterTextPaddingRight = gutterTextPaddingRight;
-  }
-
-  public Style getString() {
-    return string;
-  }
-
-  public void setString(Style string) {
-    if (string == null) {
-      throw new NullPointerException("argument 'string' cannot be null");
-    }
-    this.string = string;
-  }
-
-  public Style getKeyword() {
-    return keyword;
-  }
-
-  public void setKeyword(Style keyword) {
-    if (keyword == null) {
-      throw new NullPointerException("argument 'keyword' cannot be null");
-    }
-    this.keyword = keyword;
-  }
-
-  public Style getComment() {
-    return comment;
-  }
-
-  public void setComment(Style comment) {
-    if (comment == null) {
-      throw new NullPointerException("argument 'comment' cannot be null");
-    }
-    this.comment = comment;
-  }
-
-  public Style getType() {
-    return type;
-  }
-
-  public void setType(Style type) {
-    if (type == null) {
-      throw new NullPointerException("argument 'type' cannot be null");
-    }
-    this.type = type;
-  }
-
-  public Style getLiteral() {
-    return literal;
-  }
-
-  public void setLiteral(Style literal) {
-    if (literal == null) {
-      throw new NullPointerException("argument 'literal' cannot be null");
-    }
-    this.literal = literal;
-  }
-
-  public Style getPunctuation() {
-    return punctuation;
-  }
-
-  public void setPunctuation(Style punctuation) {
-    if (punctuation == null) {
-      throw new NullPointerException("argument 'punctuation' cannot be null");
-    }
-    this.punctuation = punctuation;
-  }
-
-  public Style getPlain() {
-    return plain;
-  }
-
-  public void setPlain(Style plain) {
-    if (plain == null) {
-      throw new NullPointerException("argument 'plain' cannot be null");
-    }
-    this.plain = plain;
-  }
-
-  public Style getTag() {
-    return tag;
-  }
-
-  public void setTag(Style tag) {
-    if (tag == null) {
-      throw new NullPointerException("argument 'tag' cannot be null");
-    }
-    this.tag = tag;
-  }
-
-  public Style getDeclaration() {
-    return declaration;
-  }
-
-  public void setDeclaration(Style declaration) {
-    if (declaration == null) {
-      throw new NullPointerException("argument 'declaration' cannot be null");
-    }
-    this.declaration = declaration;
-  }
-
-  public Style getSource() {
-    return source;
-  }
-
-  public void setSource(Style source) {
-    if (source == null) {
-      throw new NullPointerException("argument 'source' cannot be null");
-    }
-    this.source = source;
-  }
-
-  public Style getAttributeName() {
-    return attributeName;
-  }
-
-  public void setAttributeName(Style attributeName) {
-    if (attributeName == null) {
-      throw new NullPointerException("argument 'attributeName' cannot be null");
-    }
-    this.attributeName = attributeName;
-  }
-
-  public Style getAttributeValue() {
-    return attributeValue;
-  }
-
-  public void setAttributeValue(Style attributeValue) {
-    if (attributeValue == null) {
-      throw new NullPointerException("argument 'attributeValue' cannot be null");
-    }
-    this.attributeValue = attributeValue;
-  }
-
-  public Style getNoCode() {
-    return noCode;
-  }
-
-  public void setNoCode(Style noCode) {
-    if (noCode == null) {
-      throw new NullPointerException("argument 'noCode' cannot be null");
-    }
-    this.noCode = noCode;
-  }
-
-  public Style getOpenBracket() {
-    return openBracket;
-  }
-
-  public void setOpenBracket(Style openBracket) {
-    if (openBracket == null) {
-      throw new NullPointerException("argument 'openBracket' cannot be null");
-    }
-    this.openBracket = openBracket;
-  }
-
-  public Style getCloseBracket() {
-    return closeBracket;
-  }
-
-  public void setCloseBracket(Style closeBracket) {
-    if (closeBracket == null) {
-      throw new NullPointerException("argument 'closeBracket' cannot be null");
-    }
-    this.closeBracket = closeBracket;
-  }
-
-  public Style getVariable() {
-    return variable;
-  }
-
-  public void setVariable(Style variable) {
-    if (variable == null) {
-      throw new NullPointerException("argument 'variable' cannot be null");
-    }
-    this.variable = variable;
-  }
-
-  public Style getFunction() {
-    return function;
-  }
-
-  public void setFunction(Style function) {
-    if (function == null) {
-      throw new NullPointerException("argument 'function' cannot be null");
-    }
-    this.function = function;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public Theme clone() {
-    Theme object = null;
-    try {
-      object = (Theme) super.clone();
-      object.font = font;
-      object.background = background;
-      object.highlightedBackground = highlightedBackground;
-      object.gutterText = gutterText;
-      object.gutterBorderColor = gutterBorderColor;
-      object.gutterBorderWidth = gutterBorderWidth;
-      object.gutterTextFont = gutterTextFont;
-      object.gutterTextPaddingLeft = gutterTextPaddingLeft;
-      object.gutterTextPaddingRight = gutterTextPaddingRight;
-      object.string = string.clone();
-      object.keyword = keyword.clone();
-      object.comment = comment.clone();
-      object.type = type.clone();
-      object.literal = literal.clone();
-      object.punctuation = punctuation.clone();
-      object.plain = plain.clone();
-      object.tag = tag.clone();
-      object.declaration = declaration.clone();
-      object.source = source.clone();
-      object.attributeName = attributeName.clone();
-      object.attributeValue = attributeValue.clone();
-      object.noCode = noCode.clone();
-      object.openBracket = openBracket.clone();
-      object.closeBracket = closeBracket.clone();
-      object.variable = variable.clone();
-      object.function = function.clone();
-    } catch (CloneNotSupportedException ex) {
-      if (debug) {
-        Logger.getLogger(Theme.class.getName()).log(Level.WARNING, null, ex);
-      }
-    }
-    return object;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-
-    sb.append(getClass().getName());
-    sb.append(": ");
-    sb.append("font: ").append(getFont());
-    sb.append(", ");
-    sb.append("background: ").append(getBackground());
-    sb.append(", ");
-    sb.append("highlightedBackground: ").append(getHighlightedBackground());
-    sb.append(", ");
-    sb.append("gutterText: ").append(getGutterText());
-    sb.append(", ");
-    sb.append("gutterBorderColor: ").append(getGutterBorderColor());
-    sb.append(", ");
-    sb.append("gutterBorderWidth: ").append(getGutterBorderWidth());
-    sb.append(", ");
-    sb.append("gutterTextFont: ").append(getGutterTextFont());
-    sb.append(", ");
-    sb.append("gutterTextPaddingLeft: ").append(getGutterTextPaddingLeft());
-    sb.append(", ");
-    sb.append("gutterTextPaddingRight: ").append(getGutterTextPaddingRight());
-    sb.append(", ");
-    sb.append("string: ").append(getString());
-    sb.append(", ");
-    sb.append("keyword: ").append(getKeyword());
-    sb.append(", ");
-    sb.append("comment: ").append(getComment());
-    sb.append(", ");
-    sb.append("type: ").append(getType());
-    sb.append(", ");
-    sb.append("literal: ").append(getLiteral());
-    sb.append(", ");
-    sb.append("punctuation: ").append(getPunctuation());
-    sb.append(", ");
-    sb.append("plain: ").append(getPlain());
-    sb.append(", ");
-    sb.append("tag: ").append(getTag());
-    sb.append(", ");
-    sb.append("declaration: ").append(getDeclaration());
-    sb.append(", ");
-    sb.append("source: ").append(getSource());
-    sb.append(", ");
-    sb.append("attributeName: ").append(getAttributeName());
-    sb.append(", ");
-    sb.append("attributeValue: ").append(getAttributeValue());
-    sb.append(", ");
-    sb.append("noCode: ").append(getNoCode());
-    sb.append(", ");
-    sb.append("openBracket: ").append(getOpenBracket());
-    sb.append(", ");
-    sb.append("closeBracket: ").append(getCloseBracket());
-    sb.append(", ");
-    sb.append("variable: ").append(getVariable());
-    sb.append(", ");
-    sb.append("function: ").append(getFunction());
-
-    return sb.toString();
-  }
-}
diff --git a/src/prettify/theme/ThemeDefault.java b/src/prettify/theme/ThemeDefault.java
index 8a903a5..c57f1d9 100644
--- a/src/prettify/theme/ThemeDefault.java
+++ b/src/prettify/theme/ThemeDefault.java
@@ -15,6 +15,8 @@
 
 import java.awt.Color;
 import java.awt.Font;
+import syntaxhighlight.Style;
+import syntaxhighlight.Theme;
 
 /**
  * Default theme.
@@ -38,64 +40,65 @@
 
     Style plainStyle = new Style();
     plainStyle.setColor(Color.decode("0x000000"));
+    addStyle("pln", plainStyle);
     setPlain(plainStyle);
 
     Style style;
 
     style = new Style();
     style.setColor(Color.decode("0x008800"));
-    setString(style);
+    addStyle("str", style);
 
     style = new Style();
     style.setColor(Color.decode("0x000088"));
-    setKeyword(style);
+    addStyle("kwd", style);
 
     style = new Style();
     style.setColor(Color.decode("0x880000"));
-    setComment(style);
+    addStyle("com", style);
 
     style = new Style();
     style.setColor(Color.decode("0x660066"));
-    setType(style);
+    addStyle("typ", style);
 
     style = new Style();
     style.setColor(Color.decode("0x006666"));
-    setLiteral(style);
+    addStyle("lit", style);
 
     style = new Style();
     style.setColor(Color.decode("0x666600"));
-    setPunctuation(style);
+    addStyle("pun", style);
 
     style = new Style();
     style.setColor(Color.decode("0x000088"));
-    setTag(style);
+    addStyle("tag", style);
 
-    setDeclaration(plainStyle);
+    addStyle("dec", plainStyle);
 
     style = new Style();
     style.setColor(Color.decode("0x660066"));
-    setAttributeName(style);
+    addStyle("atn", style);
 
     style = new Style();
     style.setColor(Color.decode("0x008800"));
-    setAttributeValue(style);
+    addStyle("atv", style);
 
-    setNoCode(plainStyle);
+    addStyle("nocode", plainStyle);
 
     style = new Style();
     style.setColor(Color.decode("0x666600"));
-    setOpenBracket(style);
+    addStyle("opn", style);
 
     style = new Style();
     style.setColor(Color.decode("0x666600"));
-    setCloseBracket(style);
+    addStyle("clo", style);
 
     style = new Style();
     style.setColor(Color.decode("0x660066"));
-    setVariable(style);
+    addStyle("var", style);
 
     style = new Style();
     style.setColor(Color.red);
-    setFunction(style);
+    addStyle("fun", style);
   }
 }
diff --git a/src/prettify/theme/ThemeDesert.java b/src/prettify/theme/ThemeDesert.java
index 77149ea..ff1ffda 100644
--- a/src/prettify/theme/ThemeDesert.java
+++ b/src/prettify/theme/ThemeDesert.java
@@ -15,6 +15,8 @@
 
 import java.awt.Color;
 import java.awt.Font;
+import syntaxhighlight.Style;
+import syntaxhighlight.Theme;
 
 /**
  * Desert theme.
@@ -40,64 +42,65 @@
 
     Style plainStyle = new Style();
     plainStyle.setColor(Color.decode("0xffffff"));
+    addStyle("pln", plainStyle);
     setPlain(plainStyle);
 
     Style style;
 
     style = new Style();
     style.setColor(Color.decode("0xffa0a0")); /* string  - pink */
-    setString(style);
+    addStyle("str", style);
 
     style = new Style();
     style.setColor(Color.decode("0xf0e68c"));
     style.setBold(true);
-    setKeyword(style);
+    addStyle("kwd", style);
 
     style = new Style();
     style.setColor(Color.decode("0x87ceeb")); /* comment - skyblue */
-    setComment(style);
+    addStyle("com", style);
 
     style = new Style();
     style.setColor(Color.decode("0x98fb98")); /* type    - lightgreen */
-    setType(style);
+    addStyle("typ", style);
 
     style = new Style();
     style.setColor(Color.decode("0xcd5c5c")); /* literal - darkred */
-    setLiteral(style);
+    addStyle("lit", style);
 
     style = new Style();
     style.setColor(Color.decode("0xffffff"));
-    setPunctuation(style);
+    addStyle("pun", style);
 
     style = new Style();
     style.setColor(Color.decode("0xf0e68c"));/* html/xml tag    - lightyellow */
     style.setBold(true);
-    setTag(style);
+    addStyle("tag", style);
 
     style = new Style();
     style.setColor(Color.decode("0x98fb98")); /* decimal         - lightgreen */
-    setDeclaration(style);
+    addStyle("dec", style);
 
     style = new Style();
     style.setColor(Color.decode("0xbdb76b")); /* attribute name  - khaki */
     style.setBold(true);
-    setAttributeName(style);
+    addStyle("atn", style);
 
     style = new Style();
     style.setColor(Color.decode("0xffa0a0")); /* attribute value - pink */
     style.setBold(true);
-    setAttributeValue(style);
+    addStyle("atv", style);
 
     style = new Style();
     style.setColor(Color.decode("0x333333"));
-    setNoCode(style);
+    addStyle("nocode", style);
 
-    setOpenBracket(plainStyle);
+    addStyle("opn", plainStyle);
 
-    setCloseBracket(plainStyle);
+    addStyle("clo", plainStyle);
 
-    setVariable(plainStyle);
+    addStyle("var", plainStyle);
 
-    setFunction(plainStyle);
+    addStyle("fun", plainStyle);
   }
 }
diff --git a/src/prettify/theme/ThemeSonsOfObsidian.java b/src/prettify/theme/ThemeSonsOfObsidian.java
index 6faa494..1d87dac 100644
--- a/src/prettify/theme/ThemeSonsOfObsidian.java
+++ b/src/prettify/theme/ThemeSonsOfObsidian.java
@@ -15,6 +15,8 @@
 
 import java.awt.Color;
 import java.awt.Font;
+import syntaxhighlight.Style;
+import syntaxhighlight.Theme;
 
 /**
  * Son of Obsidian theme.
@@ -47,58 +49,59 @@
 
     Style plainStyle = new Style();
     plainStyle.setColor(Color.decode("0xF1F2F3"));
+    addStyle("pln", plainStyle);
     setPlain(plainStyle);
 
     Style style;
 
     style = new Style();
     style.setColor(Color.decode("0xEC7600"));
-    setString(style);
+    addStyle("str", style);
 
     style = new Style();
     style.setColor(Color.decode("0x93C763"));
-    setKeyword(style);
+    addStyle("kwd", style);
 
     style = new Style();
     style.setColor(Color.decode("0x66747B"));
-    setComment(style);
+    addStyle("com", style);
 
     style = new Style();
     style.setColor(Color.decode("0x678CB1"));
-    setType(style);
+    addStyle("typ", style);
 
     style = new Style();
     style.setColor(Color.decode("0xFACD22"));
-    setLiteral(style);
+    addStyle("lit", style);
 
     style = new Style();
     style.setColor(Color.decode("0xF1F2F3"));
-    setPunctuation(style);
+    addStyle("pun", style);
 
     style = new Style();
     style.setColor(Color.decode("0x8AC763"));
-    setTag(style);
+    addStyle("tag", style);
 
     style = new Style();
     style.setColor(Color.decode("0x800080"));
-    setDeclaration(style);
+    addStyle("dec", style);
 
     style = new Style();
     style.setColor(Color.decode("0xE0E2E4"));
-    setAttributeName(style);
+    addStyle("atn", style);
 
     style = new Style();
     style.setColor(Color.decode("0xEC7600"));
-    setAttributeValue(style);
+    addStyle("atv", style);
 
-    setNoCode(plainStyle);
+    addStyle("nocode", plainStyle);
 
-    setOpenBracket(plainStyle);
+    addStyle("opn", plainStyle);
 
-    setCloseBracket(plainStyle);
+    addStyle("clo", plainStyle);
 
-    setVariable(plainStyle);
+    addStyle("var", plainStyle);
 
-    setFunction(plainStyle);
+    addStyle("fun", plainStyle);
   }
 }
diff --git a/src/prettify/theme/ThemeSunburst.java b/src/prettify/theme/ThemeSunburst.java
index 435bf7c..6160ac0 100644
--- a/src/prettify/theme/ThemeSunburst.java
+++ b/src/prettify/theme/ThemeSunburst.java
@@ -15,6 +15,8 @@
 
 import java.awt.Color;
 import java.awt.Font;
+import syntaxhighlight.Style;
+import syntaxhighlight.Theme;
 
 /**
  * Sunbrust theme.
@@ -43,59 +45,60 @@
 
     Style plainStyle = new Style();
     plainStyle.setColor(Color.decode("0xffffff"));
+    addStyle("pln", plainStyle);
     setPlain(plainStyle);
 
     Style style;
 
     style = new Style();
     style.setColor(Color.decode("0x65B042")); /* string  - green */
-    setString(style);
+    addStyle("str", style);
 
     style = new Style();
     style.setColor(Color.decode("0xE28964")); /* keyword - dark pink */
-    setKeyword(style);
+    addStyle("kwd", style);
 
     style = new Style();
     style.setColor(Color.decode("0xAEAEAE")); /* comment - gray */
     style.setItalic(true);
-    setComment(style);
+    addStyle("com", style);
 
     style = new Style();
     style.setColor(Color.decode("0x89bdff")); /* type - light blue */
-    setType(style);
+    addStyle("typ", style);
 
     style = new Style();
     style.setColor(Color.decode("0x3387CC")); /* literal - blue */
-    setLiteral(style);
+    addStyle("lit", style);
 
     style = new Style();
     style.setColor(Color.decode("0xffffff")); /* punctuation - white */
-    setPunctuation(style);
+    addStyle("pun", style);
 
     style = new Style();
     style.setColor(Color.decode("0x89bdff")); /* html/xml tag    - light blue */
-    setTag(style);
+    addStyle("tag", style);
 
     style = new Style();
     style.setColor(Color.decode("0x3387CC")); /* decimal - blue */
-    setDeclaration(style);
+    addStyle("dec", style);
 
     style = new Style();
     style.setColor(Color.decode("0xbdb76b")); /* html/xml attribute name  - khaki */
-    setAttributeName(style);
+    addStyle("atn", style);
 
     style = new Style();
     style.setColor(Color.decode("0x65B042")); /* html/xml attribute value - green */
-    setAttributeValue(style);
+    addStyle("atv", style);
 
-    setNoCode(plainStyle);
+    addStyle("nocode", plainStyle);
 
-    setOpenBracket(plainStyle);
+    addStyle("opn", plainStyle);
 
-    setCloseBracket(plainStyle);
+    addStyle("clo", plainStyle);
 
-    setVariable(plainStyle);
+    addStyle("var", plainStyle);
 
-    setFunction(plainStyle);
+    addStyle("fun", plainStyle);
   }
 }
diff --git a/src/prettify/gui/JTextComponentRowHeader.java b/src/syntaxhighlight/JTextComponentRowHeader.java
similarity index 99%
rename from src/prettify/gui/JTextComponentRowHeader.java
rename to src/syntaxhighlight/JTextComponentRowHeader.java
index 711febe..2e76e8e 100644
--- a/src/prettify/gui/JTextComponentRowHeader.java
+++ b/src/syntaxhighlight/JTextComponentRowHeader.java
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package prettify.gui;
+package syntaxhighlight;
 
 import java.awt.Color;
 import java.awt.Container;
diff --git a/src/syntaxhighlight/ParseResult.java b/src/syntaxhighlight/ParseResult.java
new file mode 100644
index 0000000..a731c73
--- /dev/null
+++ b/src/syntaxhighlight/ParseResult.java
@@ -0,0 +1,148 @@
+// Copyright (c) 2011 Chan Wai Shing
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+package syntaxhighlight;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Matched result, it will be generated when parsing the content.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class ParseResult {
+
+  /**
+   * The start position in the document for this matched result.
+   */
+  protected int offset;
+  /**
+   * The length of the matched result.
+   */
+  protected int length;
+  /**
+   * The style key for this matched result, see {@link syntaxhighlighter.theme}.
+   */
+  protected List<String> styleKeys;
+
+  /**
+   * Constructor.
+   * 
+   * @param offset the position in the document for this matched result
+   * @param length the length of the matched result.
+   * @param styleKeys the style key for this matched result, cannot be null, see 
+   * {@link syntaxhighlighter.theme}
+   */
+  public ParseResult(int offset, int length, List<String> styleKeys) {
+    this.offset = offset;
+    this.length = length;
+    this.styleKeys = new ArrayList<String>(styleKeys);
+  }
+
+  /**
+   * The position in the document for this matched result.
+   * @return the offset in the document
+   */
+  public int getOffset() {
+    return offset;
+  }
+
+  /**
+   * The position in the document for this matched result.
+   * @param offset the offset in the document
+   */
+  public void setOffset(int offset) {
+    this.offset = offset;
+  }
+
+  /**
+   * The length of the matched result.
+   * @return the length
+   */
+  public int getLength() {
+    return length;
+  }
+
+  /**
+   * The length of the matched result.
+   * @param length the length
+   */
+  public void setLength(int length) {
+    this.length = length;
+  }
+
+  public String getStyleKeysString() {
+    StringBuilder sb = new StringBuilder(10);
+    for (int i = 0, iEnd = styleKeys.size(); i < iEnd; i++) {
+      if (i != 0) {
+        sb.append(" ");
+      }
+      sb.append(styleKeys.get(i));
+    }
+    return sb.toString();
+  }
+
+  public void setStyleKeys(List<String> styleKeys) {
+    this.styleKeys = new ArrayList<String>(styleKeys);
+  }
+
+  public boolean addStyleKey(String styleKey) {
+    return styleKeys.add(styleKey);
+  }
+
+  public boolean removeStyleKey(String styleKey) {
+    return styleKeys.remove(styleKey);
+  }
+
+  public void clearStyleKeys() {
+    styleKeys.clear();
+  }
+
+  /**
+   * The style keys for this matched result, see {@link syntaxhighlighter.theme}.
+   * @return the style keys
+   */
+  public List<String> getStyleKeys() {
+    return new ArrayList<String>(styleKeys);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+
+    sb.append("[");
+    sb.append(offset);
+    sb.append("; ");
+    sb.append(length);
+    sb.append("; ");
+    for (int i = 0, iEnd = styleKeys.size(); i < iEnd; i++) {
+      if (i != 0) {
+        sb.append(", ");
+      }
+      sb.append(styleKeys.get(i));
+    }
+    sb.append("]");
+
+    return sb.toString();
+  }
+}
\ No newline at end of file
diff --git a/src/syntaxhighlight/Parser.java b/src/syntaxhighlight/Parser.java
new file mode 100644
index 0000000..41eee05
--- /dev/null
+++ b/src/syntaxhighlight/Parser.java
@@ -0,0 +1,19 @@
+package syntaxhighlight;
+
+import java.util.List;
+
+/**
+ * The parser interface for syntax highlight.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public interface Parser {
+
+  /**
+   * Parse the {@code content} and return the parsed result.
+   * @param fileExtension the file extension of the content, null means not provided
+   * @param content the content
+   * @return the parsed result
+   */
+  List<ParseResult> parse(String fileExtension, String content);
+}
diff --git a/src/prettify/theme/Style.java b/src/syntaxhighlight/Style.java
similarity index 67%
rename from src/prettify/theme/Style.java
rename to src/syntaxhighlight/Style.java
index a29f3ab..88f4531 100644
--- a/src/prettify/theme/Style.java
+++ b/src/syntaxhighlight/Style.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2011 Chan Wai Shing
+// Copyright (C) 2012 Chan Wai Shing
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -11,19 +11,22 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package prettify.theme;
+package syntaxhighlight;
 
 import java.awt.Color;
 import javax.swing.text.SimpleAttributeSet;
 import javax.swing.text.StyleConstants;
 
 /**
- * The style used by {@link prettify.theme} as those of CSS styles.
+ * The style used by {@link syntaxhiglight.Theme} as those of CSS styles.
  * 
  * @author Chan Wai Shing <cws1989@gmail.com>
  */
-public class Style {
+public class Style implements Cloneable {
 
+  protected boolean changed;
+  protected SimpleAttributeSet attributeSet;
+  //
   protected boolean bold;
   protected Color color;
   /**
@@ -47,6 +50,9 @@
    * </p>
    */
   public Style() {
+    changed = true;
+    attributeSet = null;
+
     bold = false;
     color = Color.black;
     background = null;
@@ -55,38 +61,21 @@
   }
 
   /**
-   * Apply the style to the AttributeSet.
-   * Note that the AttributeSet should only be set by this function once or some unexpected style may appear.
-   * @param attributeSet the AttributeSet to set the style on
-   */
-  public void setAttributeSet(SimpleAttributeSet attributeSet) {
-    if (attributeSet == null) {
-      return;
-    }
-    StyleConstants.setBold(attributeSet, bold);
-    StyleConstants.setForeground(attributeSet, color);
-    if (background != null) {
-      StyleConstants.setBackground(attributeSet, background);
-    } else {
-      attributeSet.removeAttribute(StyleConstants.Background);
-    }
-    StyleConstants.setUnderline(attributeSet, underline);
-    StyleConstants.setItalic(attributeSet, italic);
-  }
-
-  /**
    * Get the AttributeSet from this style.
    * @return the AttributeSet
    */
   public SimpleAttributeSet getAttributeSet() {
-    SimpleAttributeSet attributeSet = new SimpleAttributeSet();
-    StyleConstants.setBold(attributeSet, bold);
-    StyleConstants.setForeground(attributeSet, color);
-    if (background != null) {
-      StyleConstants.setBackground(attributeSet, background);
+    if (changed) {
+      attributeSet = new SimpleAttributeSet();
+      StyleConstants.setBold(attributeSet, bold);
+      StyleConstants.setForeground(attributeSet, color);
+      if (background != null) {
+        StyleConstants.setBackground(attributeSet, background);
+      }
+      StyleConstants.setUnderline(attributeSet, underline);
+      StyleConstants.setItalic(attributeSet, italic);
+      changed = false;
     }
-    StyleConstants.setUnderline(attributeSet, underline);
-    StyleConstants.setItalic(attributeSet, italic);
     return attributeSet;
   }
 
@@ -103,9 +92,7 @@
    * @param background input null means do not set the background
    */
   public void setBackground(Color background) {
-    if (background == null) {
-      throw new NullPointerException("argument 'background' cannot be null");
-    }
+    changed = true;
     this.background = background;
   }
 
@@ -114,6 +101,7 @@
   }
 
   public void setBold(boolean bold) {
+    changed = true;
     this.bold = bold;
   }
 
@@ -125,6 +113,7 @@
     if (color == null) {
       throw new NullPointerException("argument 'color' cannot be null");
     }
+    changed = true;
     this.color = color;
   }
 
@@ -133,6 +122,7 @@
   }
 
   public void setItalic(boolean italic) {
+    changed = true;
     this.italic = italic;
   }
 
@@ -141,6 +131,7 @@
   }
 
   public void setUnderline(boolean underline) {
+    changed = true;
     this.underline = underline;
   }
 
@@ -149,12 +140,12 @@
    */
   @Override
   public int hashCode() {
-    int hash = 5;
-    hash = 59 * hash + (this.bold ? 1 : 0);
-    hash = 59 * hash + (this.color != null ? this.color.hashCode() : 0);
-    hash = 59 * hash + (this.background != null ? this.background.hashCode() : 0);
-    hash = 59 * hash + (this.underline ? 1 : 0);
-    hash = 59 * hash + (this.italic ? 1 : 0);
+    int hash = 7;
+    hash = 97 * hash + (this.bold ? 1 : 0);
+    hash = 97 * hash + (this.color != null ? this.color.hashCode() : 0);
+    hash = 97 * hash + (this.background != null ? this.background.hashCode() : 0);
+    hash = 97 * hash + (this.underline ? 1 : 0);
+    hash = 97 * hash + (this.italic ? 1 : 0);
     return hash;
   }
 
@@ -182,11 +173,6 @@
     Style object = null;
     try {
       object = (Style) super.clone();
-      object.bold = bold;
-      object.color = color;
-      object.background = background;
-      object.underline = underline;
-      object.italic = italic;
     } catch (CloneNotSupportedException ex) {
     }
     return object;
@@ -199,6 +185,7 @@
   public String toString() {
     StringBuilder sb = new StringBuilder();
 
+    sb.append("[");
     sb.append(getClass().getName());
     sb.append(": ");
     sb.append("bold: ").append(bold);
@@ -210,7 +197,8 @@
     sb.append("underline: ").append(underline);
     sb.append(", ");
     sb.append("italic: ").append(italic);
+    sb.append("]");
 
     return sb.toString();
   }
-}
\ No newline at end of file
+}
diff --git a/src/prettify/SyntaxHighlighter.java b/src/syntaxhighlight/SyntaxHighlighter.java
similarity index 92%
rename from src/prettify/SyntaxHighlighter.java
rename to src/syntaxhighlight/SyntaxHighlighter.java
index c0dd605..df9638a 100644
--- a/src/prettify/SyntaxHighlighter.java
+++ b/src/syntaxhighlight/SyntaxHighlighter.java
@@ -11,13 +11,8 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-package prettify;
+package syntaxhighlight;
 
-import prettify.parser.Job;
-import prettify.parser.Prettify;
-import prettify.gui.SyntaxHighlighterPane;
-import prettify.gui.JTextComponentRowHeader;
-import prettify.theme.Theme;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -48,7 +43,7 @@
   /**
    * The Prettify object.
    */
-  protected Prettify prettify;
+  protected Parser parser;
   /**
    * The content of the syntax highlighter, null if there is no content so far.
    */
@@ -57,19 +52,21 @@
   /**
    * Constructor.
    * 
+   * @param parser 
    * @param theme the theme for the syntax highlighter
    */
-  public SyntaxHighlighter(Theme theme) {
-    this(theme, new SyntaxHighlighterPane());
+  public SyntaxHighlighter(Parser parser, Theme theme) {
+    this(parser, theme, new SyntaxHighlighterPane());
   }
 
   /**
    * Constructor.
    * 
+   * @param parser 
    * @param theme the theme for the syntax highlighter
    * @param highlighterPane the script text pane of the syntax highlighter
    */
-  public SyntaxHighlighter(Theme theme, SyntaxHighlighterPane highlighterPane) {
+  public SyntaxHighlighter(Parser parser, Theme theme, SyntaxHighlighterPane highlighterPane) {
     super();
 
     if (theme == null) {
@@ -80,7 +77,7 @@
     }
 
     this.theme = theme;
-    prettify = new Prettify();
+    this.parser = parser;
 
     setBorder(null);
 
@@ -103,9 +100,7 @@
     if (content != null) {
       // stop the change listener on the row header to speed up rendering
       highlighterRowHeader.setListenToDocumentUpdate(false);
-      Job job = new Job(0, content);
-      prettify.langHandlerForExtension(null, content).decorate(job);
-      highlighter.setStyle(job.getDecorations());
+      highlighter.setStyle(parser.parse(null, content));
       // resume the change listener on the row header
       highlighterRowHeader.setListenToDocumentUpdate(true);
       // notify the row header to update its information related to the SyntaxHighlighterPane
diff --git a/src/prettify/gui/SyntaxHighlighterPane.java b/src/syntaxhighlight/SyntaxHighlighterPane.java
similarity index 84%
rename from src/prettify/gui/SyntaxHighlighterPane.java
rename to src/syntaxhighlight/SyntaxHighlighterPane.java
index d3c0dd0..482b87f 100644
--- a/src/prettify/gui/SyntaxHighlighterPane.java
+++ b/src/syntaxhighlight/SyntaxHighlighterPane.java
@@ -1,19 +1,25 @@
-// Copyright (C) 2011 Chan Wai Shing
+// Copyright (c) 2011 Chan Wai Shing
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
 //
-//      http://www.apache.org/licenses/LICENSE-2.0
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
 //
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-package prettify.gui;
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+package syntaxhighlight;
 
-import prettify.theme.Theme;
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.FontMetrics;
@@ -24,7 +30,9 @@
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseMotionListener;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.swing.JTextPane;
@@ -83,7 +91,7 @@
   /**
    * The style list.
    */
-  protected List<Object> styleList;
+  protected Map<String, List<ParseResult>> styleList;
   /**
    * Record the mouse cursor is currently pointing at which line of the 
    * document. -1 means not any line.
@@ -311,19 +319,32 @@
     styleList = null;
   }
 
-  /**
-   * Apply the list of style to the script text pane.
-   * 
-   * @param styleList the style list
-   */
-  public void setStyle(List<Object> styleList) {
+  public void setStyle(List<ParseResult> styleList) {
     if (styleList == null) {
-      this.styleList = new ArrayList<Object>();
-      return;
+      throw new NullPointerException("argumenst 'styleList' cannot be null");
     }
-    this.styleList = new ArrayList<Object>(styleList);
 
-    if (theme == null) {
+    this.styleList = new HashMap<String, List<ParseResult>>();
+
+    for (ParseResult parseResult : styleList) {
+      String styleKeysString = parseResult.getStyleKeysString();
+      List<ParseResult> _styleList = this.styleList.get(styleKeysString);
+      if (_styleList == null) {
+        _styleList = new ArrayList<ParseResult>();
+        this.styleList.put(styleKeysString, _styleList);
+      }
+      _styleList.add(parseResult);
+    }
+
+    applyStyle();
+  }
+
+  /**
+   * Apply the list of style to the script text pane. See 
+   * {@link syntaxhighlighter.parser.Parser#parse(syntaxhighlighter.brush.Brush, boolean, char[], int, int)}.
+   */
+  protected void applyStyle() {
+    if (theme == null || styleList == null) {
       return;
     }
 
@@ -331,13 +352,14 @@
     // clear all the existing style
     document.setCharacterAttributes(0, document.getLength(), theme.getPlain().getAttributeSet(), true);
 
-    Integer startPos = 0, endPos = 0;
     // apply style according to the style list
-    for (int i = 0, iEnd = styleList.size(); i < iEnd; i += 2) {
-      SimpleAttributeSet attributeSet = theme.getStyle((String) styleList.get(i + 1)).getAttributeSet();
-      endPos = i + 2 < iEnd ? (Integer) styleList.get(i + 2) : document.getLength();
-      startPos = (Integer) styleList.get(i);
-      document.setCharacterAttributes(startPos, endPos - startPos, attributeSet, true);
+    for (String key : styleList.keySet()) {
+      List<ParseResult> posList = styleList.get(key);
+
+      SimpleAttributeSet attributeSet = theme.getStyle(key).getAttributeSet();
+      for (ParseResult pos : posList) {
+        document.setCharacterAttributes(pos.getOffset(), pos.getLength(), attributeSet, true);
+      }
     }
 
     repaint();
@@ -368,7 +390,7 @@
     setHighlightedBackground(theme.getHighlightedBackground());
 
     if (styleList != null) {
-      setStyle(styleList);
+      applyStyle();
     }
   }
 
@@ -456,11 +478,12 @@
    * @param highlightedLineList the list that contain the highlighted lines
    */
   public void setHighlightedLineList(List<Integer> highlightedLineList) {
+    if (highlightedLineList == null) {
+      throw new NullPointerException("argument 'highlightedLineList' cannot be null");
+    }
     synchronized (this.highlightedLineList) {
       this.highlightedLineList.clear();
-      if (highlightedLineList != null) {
-        this.highlightedLineList.addAll(highlightedLineList);
-      }
+      this.highlightedLineList.addAll(highlightedLineList);
     }
     repaint();
   }
diff --git a/src/syntaxhighlight/Theme.java b/src/syntaxhighlight/Theme.java
new file mode 100644
index 0000000..0449e15
--- /dev/null
+++ b/src/syntaxhighlight/Theme.java
@@ -0,0 +1,401 @@
+// Copyright (c) 2011 Chan Wai Shing
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+package syntaxhighlight;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+import javax.swing.text.SimpleAttributeSet;
+
+/**
+ * Theme for the syntax highlighter.
+ * To make a new theme, either extending this class of initiate this class 
+ * and set the parameter by the setter. For the default value, please refer to 
+ * the constructor.
+ * 
+ * @author Chan Wai Shing <cws1989@gmail.com>
+ */
+public class Theme {
+
+  private static final Logger LOG = Logger.getLogger(Theme.class.getName());
+  /**
+   * The font of the script text.
+   */
+  protected Font font;
+  /**
+   * The background color of the script text area.
+   */
+  protected Color background;
+  /**
+   * The background color of the highlighted line of script text.
+   */
+  protected Color highlightedBackground;
+  /**
+   * Gutter (line number column on the left)
+   */
+  /**
+   * The color of the gutter text.
+   */
+  protected Color gutterText;
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   */
+  protected Color gutterBorderColor;
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   */
+  protected int gutterBorderWidth;
+  /**
+   * The font of the gutter text.
+   */
+  protected Font gutterTextFont;
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 
+   * 'the left margin'.
+   */
+  protected int gutterTextPaddingLeft;
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   */
+  protected int gutterTextPaddingRight;
+  protected Style plain;
+  protected Map<String, Style> styles;
+
+  /**
+   * Constructor.<br />
+   * <p>
+   * <b>Default value:</b><br />
+   * <ul>
+   * <li>font: Consolas 12pt</li>
+   * <li>background: white</li>
+   * <li>gutter text: black</li>
+   * <li>gutter border: R: 184, G: 184, B: 184</li>
+   * <li>gutter border width: 3px</li>
+   * <li>gutter text font: Consolas 12pt</li>
+   * <li>gutter text padding-left: 7px</li>
+   * <li>gutter text padding-right: 7px</li>
+   * </ul>
+   * </p>
+   */
+  public Theme() {
+    font = new Font("Consolas", Font.PLAIN, 12);
+    background = Color.white;
+
+    highlightedBackground = Color.gray;
+
+    gutterText = Color.black;
+    gutterBorderColor = new Color(184, 184, 184);
+    gutterBorderWidth = 3;
+    gutterTextFont = new Font("Consolas", Font.PLAIN, 12);
+    gutterTextPaddingLeft = 7;
+    gutterTextPaddingRight = 7;
+
+    plain = new Style();
+
+    styles = new HashMap<String, Style>();
+  }
+
+  /**
+   * Apply the theme to the row header panel.
+   * @param rowHeader the row header to apply theme on
+   */
+  public void setTheme(JTextComponentRowHeader rowHeader) {
+    rowHeader.setBackground(background);
+    rowHeader.setHighlightedColor(background);
+
+    rowHeader.setForeground(gutterText);
+    rowHeader.setBorderColor(gutterBorderColor);
+    rowHeader.setBorderWidth(gutterBorderWidth);
+    rowHeader.setFont(gutterTextFont);
+    rowHeader.setPaddingLeft(gutterTextPaddingLeft);
+    rowHeader.setPaddingRight(gutterTextPaddingRight);
+  }
+
+  public void setPlain(Style plain) {
+    if (plain == null) {
+      throw new NullPointerException("argument 'plain' cannot be null");
+    }
+    this.plain = plain;
+  }
+
+  public Style getPlain() {
+    return plain;
+  }
+
+  public SimpleAttributeSet getStyleAttributeSet(String styleKeys) {
+    if (styleKeys.indexOf(' ') != -1) {
+      SimpleAttributeSet returnAttributeSet = new SimpleAttributeSet();
+      String[] keys = styleKeys.split(" ");
+      for (String _key : keys) {
+        returnAttributeSet.addAttributes(getStyle(_key).getAttributeSet());
+      }
+      return returnAttributeSet;
+    } else {
+      return getStyle(styleKeys).getAttributeSet();
+    }
+  }
+
+  public Style addStyle(String styleKey, Style style) {
+    return styles.put(styleKey, style);
+  }
+
+  public Style removeStyle(String styleKey) {
+    return styles.remove(styleKey);
+  }
+
+  /**
+   * Get the {@link syntaxhighlighter.theme.Style} by keyword.
+   * 
+   * @param key the keyword
+   * 
+   * @return the {@link syntaxhighlighter.theme.Style} related to the 
+   * {@code key}; if the style related to the <code>key</code> not exist, 
+   * the style of 'plain' will return.
+   */
+  public Style getStyle(String key) {
+    Style returnStyle = styles.get(key);
+    return returnStyle != null ? returnStyle : plain;
+  }
+
+  public Map<String, Style> getStyles() {
+    return new HashMap<String, Style>(styles);
+  }
+
+  public void clearStyles() {
+    styles.clear();
+  }
+
+  /**
+   * The font of the script text.
+   * @return the font
+   */
+  public Font getFont() {
+    return font;
+  }
+
+  /**
+   * The font of the script text.
+   * @param font the font
+   */
+  public void setFont(Font font) {
+    if (font == null) {
+      throw new NullPointerException("argument 'font' cannot be null");
+    }
+    this.font = font;
+  }
+
+  /**
+   * The background color of the script text area.
+   * @return the color
+   */
+  public Color getBackground() {
+    return background;
+  }
+
+  /**
+   * The background color of the script text area.
+   * @param background the color
+   */
+  public void setBackground(Color background) {
+    if (background == null) {
+      throw new NullPointerException("argument 'background' cannot be null");
+    }
+    this.background = background;
+  }
+
+  /**
+   * The background color of the highlighted line of script text.
+   * @return the color
+   */
+  public Color getHighlightedBackground() {
+    return highlightedBackground;
+  }
+
+  /**
+   * The background color of the highlighted line of script text.
+   * @param highlightedBackground the color
+   */
+  public void setHighlightedBackground(Color highlightedBackground) {
+    if (highlightedBackground == null) {
+      throw new NullPointerException("argument 'highlightedBackground' cannot be null");
+    }
+    this.highlightedBackground = highlightedBackground;
+  }
+
+  /**
+   * The color of the gutter text.
+   * @return the color
+   */
+  public Color getGutterText() {
+    return gutterText;
+  }
+
+  /**
+   * The color of the gutter text.
+   * @param gutterText the color
+   */
+  public void setGutterText(Color gutterText) {
+    if (gutterText == null) {
+      throw new NullPointerException("argument 'gutterText' cannot be null");
+    }
+    this.gutterText = gutterText;
+  }
+
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   * @return the color
+   */
+  public Color getGutterBorderColor() {
+    return gutterBorderColor;
+  }
+
+  /**
+   * The color of the border that joint the gutter and the script text panel.
+   * @param gutterBorderColor the color
+   */
+  public void setGutterBorderColor(Color gutterBorderColor) {
+    if (gutterBorderColor == null) {
+      throw new NullPointerException("argument 'gutterBorderColor' cannot be null");
+    }
+    this.gutterBorderColor = gutterBorderColor;
+  }
+
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   * @return the width in pixel
+   */
+  public int getGutterBorderWidth() {
+    return gutterBorderWidth;
+  }
+
+  /**
+   * The width of the border that joint the gutter and the script text panel.
+   * @param gutterBorderWidth in pixel
+   */
+  public void setGutterBorderWidth(int gutterBorderWidth) {
+    this.gutterBorderWidth = gutterBorderWidth;
+  }
+
+  /**
+   * The font of the gutter text.
+   * @return the font
+   */
+  public Font getGutterTextFont() {
+    return gutterTextFont;
+  }
+
+  /**
+   * The font of the gutter text.
+   * @param gutterTextFont the font
+   */
+  public void setGutterTextFont(Font gutterTextFont) {
+    if (gutterTextFont == null) {
+      throw new NullPointerException("argument 'gutterTextFont' cannot be null");
+    }
+    this.gutterTextFont = gutterTextFont;
+  }
+
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 
+   * 'the left margin'.
+   * @return the padding in pixel
+   */
+  public int getGutterTextPaddingLeft() {
+    return gutterTextPaddingLeft;
+  }
+
+  /**
+   * The minimum padding from 'the leftmost of the line number text' to 
+   * 'the left margin'.
+   * @param gutterTextPaddingLeft in pixel
+   */
+  public void setGutterTextPaddingLeft(int gutterTextPaddingLeft) {
+    this.gutterTextPaddingLeft = gutterTextPaddingLeft;
+  }
+
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   * @return the padding in pixel
+   */
+  public int getGutterTextPaddingRight() {
+    return gutterTextPaddingRight;
+  }
+
+  /**
+   * The minimum padding from 'the rightmost of the line number text' to 
+   * 'the right margin' (not to the gutter border).
+   * @param gutterTextPaddingRight in pixel
+   */
+  public void setGutterTextPaddingRight(int gutterTextPaddingRight) {
+    this.gutterTextPaddingRight = gutterTextPaddingRight;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Theme clone() {
+    Theme object = null;
+    try {
+      object = (Theme) super.clone();
+      object.styles = new HashMap<String, Style>();
+      for (String key : styles.keySet()) {
+        object.styles.put(key, styles.get(key).clone());
+      }
+    } catch (CloneNotSupportedException ex) {
+    }
+    return object;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+
+    sb.append(getClass().getName());
+    sb.append(": ");
+    sb.append("font: ").append(getFont());
+    sb.append(", ");
+    sb.append("background: ").append(getBackground());
+    sb.append(", ");
+    sb.append("highlightedBackground: ").append(getHighlightedBackground());
+    sb.append(", ");
+    sb.append("gutterText: ").append(getGutterText());
+    sb.append(", ");
+    sb.append("gutterBorderColor: ").append(getGutterBorderColor());
+    sb.append(", ");
+    sb.append("gutterBorderWidth: ").append(getGutterBorderWidth());
+    sb.append(", ");
+    sb.append("gutterTextFont: ").append(getGutterTextFont());
+    sb.append(", ");
+    sb.append("gutterTextPaddingLeft: ").append(getGutterTextPaddingLeft());
+    sb.append(", ");
+    sb.append("gutterTextPaddingRight: ").append(getGutterTextPaddingRight());
+
+    return sb.toString();
+  }
+}