gt: 		else if ((rule.getType()&IS_ESCAPE)==IS_ESCAPE)
context: /*
   * Copyright (C) 2002-2006 Stefan Stiller
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  package com.kiwisoft.utils.text;
  
  import java.util.Hashtable;
  import javax.swing.text.Segment;
  
  /**
   * A token marker splits lines of text into tokens. Each token carries
   * a length field and an identification tag that can be mapped to a color
   * or font style for painting that token.
   *
   * @author Mike Dillon
   * @author Slava Pestov
   * @author Mark Soderquist
   * @author Stefan Stiller
   * @version $Revision: 1.3 $, $Date: 2006/03/24 18:02:55 $
   * @see com.kiwisoft.utils.text.Token
   */
  public class SyntaxParser
  {
  	private static final int MAJOR_TYPES=0x000000FF;
  
  	public static final int WHITESPACE=1;
  	public static final int SPAN=2;
  	public static final int MARK_PREVIOUS=4;
  	public static final int MARK_FOLLOWING=8;
  	public static final int EOL_SPAN=16;
  
  	public static final int EXCLUDE_MATCH=1<<8;
  	public static final int AT_LINE_START=1<<9;
  	public static final int NO_LINE_BREAK=1<<10;
  	public static final int NO_WORD_BREAK=1<<11;
  	public static final int IS_ESCAPE=1<<12;
  	public static final int DELEGATE=1<<13;
  
  	private static final int SOFT_SPAN=MARK_FOLLOWING|NO_WORD_BREAK;
  
  	private String rulePrefix;
  	private Hashtable rulesMap=new Hashtable(64);
  	private SyntaxRules defaultRules;
  
  	private LineContext context;
  	private Segment pattern=new Segment();
  	private int lastOffset;
  	private int lastKeyword;
  	private int lineLength;
  	private int position;
  	private boolean escaped;
  
  	public void addRules(String name, SyntaxRules rules)
  	{
  		if (rules==null) return;
  		if (name==null) name="MAIN";
  		this.rulesMap.put(rulePrefix.concat(name), rules);
  		if ("MAIN".equals(name)) defaultRules=rules;
  	}
  
  	public SyntaxRules getDefaultSyntaxRules()
  	{
  		return defaultRules;
  	}
  
  	private SyntaxRules getSyntaxRules(String name)
  	{
  		SyntaxRules rules=(SyntaxRules)this.rulesMap.get(name);
  		if (rules==null && !name.startsWith(rulePrefix))
  		{
  			int delimiter=name.indexOf("::");
  			String defName=name.substring(0, delimiter);
  			SyntaxDefinition definition=SyntaxDefinitionFactory.getInstance().getSyntaxDefinition(defName);
  			if (definition==null)
  			{
  				System.err.println("Unknown syntax definition: "+defName);
  				rules=null;
  			}
  			else
  			{
  				SyntaxParser parser=definition.getSyntaxParser();
  				rules=parser.getSyntaxRules(name);
  				this.rulesMap.put(name, rules);
  			}
  		}
  		if (rules==null) System.err.println("Unresolved delegate target: "+name);
  		return rules;
  	}
  
  	public void setName(String name)
  	{
  		if (name==null) throw new NullPointerException();
  		rulePrefix=name.concat("::");
  	}
  
  	/**
  	 * Do not call this method directly. This class is not thread safe.
  	 * Call SyntaxDocument.markTokens() instead.
  	 * @param context The new <code>SyntaxLineContext</code> of the line to mark.
  	 */
  	public LineContext markTokens(Segment segment, LineContext context)
  	{
  		this.context=context;
  		TokenList tokenList=new TokenList();
  
  		lastOffset=lastKeyword=segment.offset;
  		lineLength=segment.count+segment.offset;
  
  		int terminateChar=this.context.getSyntaxRules().getTerminateChar();
  		int searchLimit=terminateChar>=0 && terminateChar<segment.count ? segment.offset+terminateChar : lineLength;
  
  		escaped=false;
  
  		boolean keepGoing;
  		boolean tempEscaped;
  		Segment tempPattern;
  		SyntaxRule rule;
  		LineContext tempContext;
  
  		for (position=segment.offset; position<searchLimit; position++)
  		{
  			// If we are not in the top level context, we are delegated
  			if (this.context.getParentContext()!=null)
  			{
  				tempContext=this.context;
  				this.context=this.context.getParentContext();
  
  				pattern.array=this.context.getCurrentRule().getCharacters();
  				pattern.count=this.context.getCurrentRule().getLength(1);
  				pattern.offset=this.context.getCurrentRule().getOffset(1);
  
  				keepGoing=handleRule(tokenList, segment, this.context.getCurrentRule());
  
  				this.context=tempContext;
  
  				if (!keepGoing)
  				{
  					if (escaped)
  					{
  						escaped=false;
  					}
  					else
  					{
  						if (position!=lastOffset)
  						{
  							if (this.context.getCurrentRule()==null)
  							{
  								markKeyword(tokenList, segment, lastKeyword, position);
  								tokenList.addToken(position-lastOffset, this.context.getSyntaxRules().getDefault(), this.context.getSyntaxRules());
  							}
  							else if ((this.context.getCurrentRule().getType()&(NO_LINE_BREAK|NO_WORD_BREAK))==0)
  							{
  								tokenList.addToken(position-lastOffset, this.context.getCurrentRule().getStyle(), this.context.getSyntaxRules());
  							}
  							else
  							{
  								tokenList.addToken(position-lastOffset, Style.INVALID, this.context.getSyntaxRules());
  							}
  						}
  
  						this.context=(LineContext)this.context.getParentContext().clone();
  
  						if ((this.context.getCurrentRule().getType()&EXCLUDE_MATCH)==EXCLUDE_MATCH)
  						{
  							tokenList.addToken(pattern.count, this.context.getSyntaxRules().getDefault(), this.context.getSyntaxRules());
  						}
  						else
  						{
  							tokenList.addToken(pattern.count, this.context.getCurrentRule().getStyle(), this.context.getSyntaxRules());
  						}
  
  						this.context.setCurrentRule(null);
  
  						lastKeyword=lastOffset=position+pattern.count;
  					}
  
  					// Move postion to last character of match sequence.
  					position+=pattern.count-1;
  					continue;
  				}
  			}
  
  			// Check the escape rule for the current context.
  			if ((rule=this.context.getSyntaxRules().getEscapeRule())!=null)
  			{
  				// Assign tempPattern to mutable "buffer" moPattern.
  				tempPattern=pattern;
  
  				// Swap in the escape moPattern.
  				pattern=this.context.getSyntaxRules().getEscapePattern();
  
  				tempEscaped=escaped;
  
  				keepGoing=handleRule(tokenList, segment, rule);
  
  				// Swap back the buffer moPattern.
  				pattern=tempPattern;
  
  				if (!keepGoing)
  				{
  					if (tempEscaped) escaped=false;
  					continue;
  				}
  			}
  
  			// If we are inside a span, check for its end sequence.
  			rule=this.context.getCurrentRule();
  			if (rule!=null && (rule.getType()&SPAN)==SPAN)
  			{
  				pattern.array=rule.getCharacters();
  				pattern.count=rule.getLength(1);
  				pattern.offset=rule.getOffset(1);
  
  				// If we match the end of the span, or if this is a "hard" span,
  				// continue to the next character; otherwise, check all
  				// applicable rulesMap below.
  				if (!handleRule(tokenList, segment, rule) || (rule.getType()&SOFT_SPAN)==0)
  				{
  					escaped=false;
  					continue;
  				}
  			}
  
  			// Now check every rule.
  			rule=this.context.getSyntaxRules().getRules(segment.array[position]);
  			while (rule!=null)
  			{
  				pattern.array=rule.getCharacters();
  
  				if (this.context.getCurrentRule()==rule && (rule.getType()&SPAN)==SPAN)
  				{
  					pattern.count=rule.getLength(1);
  					pattern.offset=rule.getOffset(1);
  				}
  				else
  				{
  					pattern.count=rule.getLength(0);
  					pattern.offset=0;
  				}
  
  				// Stop checking rulesMap if there was a match and go to next position.
  				if (!handleRule(tokenList, segment, rule)) break;
  
  				rule=rule.getNextRule();
  			}
  
  			escaped=false;
  		}
  		// Done scanning line for tokens.
  
  		// Check for keywords at the end of the line.
  		if (this.context.getCurrentRule()==null) markKeyword(tokenList, segment, lastKeyword, lineLength);
  
  		// Mark all remaining characters.
  		if (lastOffset!=lineLength)
  		{
  			if (this.context.getCurrentRule()==null)
  			{
  				tokenList.addToken(
  				    lineLength-lastOffset,
  				    this.context.getSyntaxRules().getDefault(),
  				    this.context.getSyntaxRules()
  				);
  			}
  			else if ((this.context.getCurrentRule().getType()&SPAN)==SPAN &&
  			    (this.context.getCurrentRule().getType()&(NO_LINE_BREAK|NO_WORD_BREAK))!=0)
  			{
  				tokenList.addToken(lineLength-lastOffset, Style.INVALID, this.context.getSyntaxRules());
  				this.context.setCurrentRule(null);
  			}
  			else
  			{
  				tokenList.addToken(lineLength-lastOffset, this.context.getCurrentRule().getStyle(), this.context.getSyntaxRules());
  				if ((this.context.getCurrentRule().getType()&MARK_FOLLOWING)==MARK_FOLLOWING)
  				{
  					this.context.setCurrentRule(null);
  				}
  			}
  		}
  		tokenList.addToken(0, Style.END, this.context.getSyntaxRules());
  		this.context.setTokenList(tokenList);
  		return this.context;
  	}
  
  	/**
  	 * Checks if the rule matches the line at the current position
  	 * and handles the rule if so.
  	 *
  	 * @param tokenList List of tokens in line.
  	 * @param segment Segment to check rule against
  	 * @param rule ParserRule to check against line
  	 * @return true: Keep checking other rulesMap. <br>false: Stop checking other rulesMap.
  	 */
  	private boolean handleRule(TokenList tokenList, Segment segment, SyntaxRule rule)
  	{
  		if (pattern.count==0) return true;
  
  		if (lineLength-position<pattern.count) return true;
  
  		for (int i=0; i<pattern.count; i++)
  		{
  			char ch1=pattern.array[pattern.offset+i];
  			char ch2=segment.array[position+i];
  
  			// Break out and check the next rule if there is a mismatch.
  			if (!(ch1==ch2 || context.getSyntaxRules().getIgnoreCase()
  			    && (Character.toLowerCase(ch1)==ch2 || ch1==Character.toLowerCase(ch2))))
  				return true;
  		}
  
  		if (escaped)
  		{
  			position+=pattern.count-1;
  			return false;
  		}
  