All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gjt.sp.jedit.syntax.XModeHandler Maven / Gradle / Ivy

Go to download

This project aims to build a command line tool that can create HTML view with syntax highlighted source code. It uses Jedit syntax highlighting engine and support all languages that are supported in JEdit. Which are currently: ActionScript, Ada 95, ANTLR, Apache HTTPD, APDL, AppleScript, ASP, Aspect-J, Assembly, AWK, B formal method, Batch, BBj, BCEL, BibTeX, C, C++, C#, CHILL, CIL, COBOL, ColdFusion, CSS, CVS Commit, D, DOxygen, DSSSL, Eiffel, EmbPerl, Erlang, Factor, Fortran, Foxpro, FreeMarker, Fortran, Gettext, Groovy, Haskell, HTML, Icon, IDL, Inform, INI, Inno Setup, Informix 4GL, Interlis, Io, Java, JavaScript, JCL, JHTML, JMK, JSP, Latex, Lilypond, Lisp, LOTOS, Lua, Makefile, Maple, ML, Modula-3, MoinMoin, MQSC, NetRexx, NQC, NSIS2, Objective C, ObjectRexx, Occam, Omnimark, Parrot, Pascal, Patch, Perl, PHP, Pike, PL-SQL, PL/I, Pop11, PostScript, Povray, PowerDynamo, Progress 4GL, Prolog, Properties, PSP, PV-WAVE, Pyrex, Python, REBOL, Redcode, Relax-NG, RelationalView, Rest, Rib, RPM spec, RTF, Ruby, Ruby-HTML, RView, S+, S#, SAS, Scheme, SDL/PL, SGML, Shell Script, SHTML, Smalltalk, SMI MIB, SQR, Squidconf, SVN Commit, Swig, TCL, TeX, Texinfo, TPL, Transact-SQL, UnrealScript, VBScript, Velocity, Verilog, VHDL, XML, XSL, ZPT

The newest version!
/*
 * XModeHandler.java - XML handler for mode files
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1999 mike dillon
 * Portions copyright (C) 2000, 2001 Slava Pestov
 *
 * 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 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 org.gjt.sp.jedit.syntax;

//{{{ Imports
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.DefaultHandler;

import org.gjt.sp.jedit.Mode;
import org.gjt.sp.util.Log;
import org.gjt.sp.util.XMLUtilities;
//}}}

/**
 * XML handler for mode definition files.
 * @version $Id: XModeHandler.java 12875 2008-06-22 10:28:48Z kpouer $
 */
public abstract class XModeHandler extends DefaultHandler
{
	//{{{ XModeHandler constructor
	public XModeHandler (String modeName)
	{
		this.modeName = modeName;
		marker = new TokenMarker();
		marker.addRuleSet(new ParserRuleSet(modeName,"MAIN"));
		stateStack = new Stack();
	} //}}}

	//{{{ resolveEntity() method
	public InputSource resolveEntity(String publicId, String systemId)
	{
		return XMLUtilities.findEntity(systemId, "xmode.dtd", XModeHandler.class);
	} //}}}

	//{{{ characters() method
	public void characters(char[] c, int off, int len)
	{
		peekElement().setText(c, off, len);
	} //}}}

	//{{{ startElement() method
	public void startElement(String uri, String localName,
				 String qName, Attributes attrs)
	{
		TagDecl tag = pushElement(qName, attrs);

		if (qName.equals("WHITESPACE"))
		{
			Log.log(Log.WARNING,this,modeName + ": WHITESPACE rule "
				+ "no longer needed");
		}
		else if (qName.equals("KEYWORDS"))
		{
			keywords = new KeywordMap(rules.getIgnoreCase());
		}
		else if (qName.equals("RULES"))
		{
			if(tag.lastSetName == null)
				tag.lastSetName = "MAIN";
			rules = marker.getRuleSet(tag.lastSetName);
			if(rules == null)
			{
				rules = new ParserRuleSet(modeName,tag.lastSetName);
				marker.addRuleSet(rules);
			}
			rules.setIgnoreCase(tag.lastIgnoreCase);
			rules.setHighlightDigits(tag.lastHighlightDigits);
			if(tag.lastDigitRE != null)
			{
				try
				{
					rules.setDigitRegexp(Pattern.compile(tag.lastDigitRE,
						tag.lastIgnoreCase
						? Pattern.CASE_INSENSITIVE : 0));
				}
				catch(PatternSyntaxException e)
				{
					error("regexp",e);
				}
			}

			if(tag.lastEscape != null)
				rules.setEscapeRule(ParserRule.createEscapeRule(tag.lastEscape));
			rules.setDefault(tag.lastDefaultID);
			rules.setNoWordSep(tag.lastNoWordSep);
		}
	} //}}}

	//{{{ endElement() method
	public void endElement(String uri, String localName, String name)
	{
		TagDecl tag = popElement();
		if (name.equals(tag.tagName))
		{
			if(tag.lastDelegateSet != null
					&& ! tag.tagName.equals("IMPORT")
					&& ! tag.lastDelegateSet.getModeName().equals(modeName))
			{
				Mode mode = ModeProvider.instance.getMode(tag.lastDelegateSet.getModeName());
				if( ! reloadModes.contains(mode) )
				{
					reloadModes.add(mode);
				}
			}
			//{{{ PROPERTY
			if (tag.tagName.equals("PROPERTY"))
			{
				props.put(propName,propValue);
			} //}}}
			//{{{ PROPS
			else if (tag.tagName.equals("PROPS"))
			{
				if(peekElement().tagName.equals("RULES"))
					rules.setProperties(props);
				else
					modeProps = props;

				props = new Hashtable();
			} //}}}
			//{{{ RULES
			else if (tag.tagName.equals("RULES"))
			{
				rules.setKeywords(keywords);
				keywords = null;
				rules = null;
			} //}}}
			//{{{ IMPORT
			else if (tag.tagName.equals("IMPORT"))
			{
				// prevent lockups
				if (!rules.equals(tag.lastDelegateSet))
				{
					rules.addRuleSet(tag.lastDelegateSet);
				}
			} //}}}
			//{{{ TERMINATE
			else if (tag.tagName.equals("TERMINATE"))
			{
				rules.setTerminateChar(tag.termChar);
			} //}}}
			//{{{ SEQ
			else if (tag.tagName.equals("SEQ"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","SEQ");
					return;
				}

				rules.addRule(ParserRule.createSequenceRule(
					tag.lastStartPosMatch,tag.lastStart.toString(),
					tag.lastDelegateSet,tag.lastTokenID));
			} //}}}
			//{{{ SEQ_REGEXP
			else if (tag.tagName.equals("SEQ_REGEXP"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","SEQ_REGEXP");
					return;
				}

				try
				{
					if (null != tag.lastHashChars)
					{
						rules.addRule(ParserRule.createRegexpSequenceRule(
							tag.lastStartPosMatch,tag.lastHashChars.toCharArray(),
							tag.lastStart.toString(),tag.lastDelegateSet,
							tag.lastTokenID,findParent("RULES").lastIgnoreCase));
					}
					else
					{
						rules.addRule(ParserRule.createRegexpSequenceRule(
							tag.lastHashChar,tag.lastStartPosMatch,
							tag.lastStart.toString(),tag.lastDelegateSet,
							tag.lastTokenID,findParent("RULES").lastIgnoreCase));
					}
				}
				catch(PatternSyntaxException re)
				{
					error("regexp",re);
				}
			} //}}}
			//{{{ SPAN
			else if (tag.tagName.equals("SPAN"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","BEGIN");
					return;
				}

				if(tag.lastEnd == null)
				{
					error("empty-tag","END");
					return;
				}

				rules.addRule(ParserRule
					.createSpanRule(
					tag.lastStartPosMatch,tag.lastStart.toString(),
					tag.lastEndPosMatch,tag.lastEnd.toString(),
					tag.lastDelegateSet,
					tag.lastTokenID,tag.lastMatchType,
					tag.lastNoLineBreak,
					tag.lastNoWordBreak,
					tag.lastEscape));
			} //}}}
			//{{{ SPAN_REGEXP
			else if (tag.tagName.equals("SPAN_REGEXP"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","BEGIN");
					return;
				}

				if(tag.lastEnd == null)
				{
					error("empty-tag","END");
					return;
				}

				try
				{
					if (null != tag.lastHashChars)
					{
						rules.addRule(ParserRule
							.createRegexpSpanRule(
							tag.lastStartPosMatch,tag.lastHashChars.toCharArray(),
							tag.lastStart.toString(),
							tag.lastEndPosMatch,tag.lastEnd.toString(),
							tag.lastDelegateSet,
							tag.lastTokenID,
							tag.lastMatchType,
							tag.lastNoLineBreak,
							tag.lastNoWordBreak,
							findParent("RULES").lastIgnoreCase,
							tag.lastEscape));
					}
					else
					{
						rules.addRule(ParserRule
							.createRegexpSpanRule(
							tag.lastHashChar,
							tag.lastStartPosMatch,tag.lastStart.toString(),
							tag.lastEndPosMatch,tag.lastEnd.toString(),
							tag.lastDelegateSet,
							tag.lastTokenID,
							tag.lastMatchType,
							tag.lastNoLineBreak,
							tag.lastNoWordBreak,
							findParent("RULES").lastIgnoreCase,
							tag.lastEscape));
					}
				}
				catch(PatternSyntaxException re)
				{
					error("regexp",re);
				}
			} //}}}
			//{{{ EOL_SPAN
			else if (tag.tagName.equals("EOL_SPAN"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","EOL_SPAN");
					return;
				}

				rules.addRule(ParserRule.createEOLSpanRule(
					tag.lastStartPosMatch,tag.lastStart.toString(),
					tag.lastDelegateSet,tag.lastTokenID,
					tag.lastMatchType));
			} //}}}
			//{{{ EOL_SPAN_REGEXP
			else if (tag.tagName.equals("EOL_SPAN_REGEXP"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","EOL_SPAN_REGEXP");
					return;
				}

				try
				{
					if (null != tag.lastHashChars)
					{
						rules.addRule(ParserRule.createRegexpEOLSpanRule(
							tag.lastStartPosMatch,tag.lastHashChars.toCharArray(),
							tag.lastStart.toString(),tag.lastDelegateSet,
							tag.lastTokenID,tag.lastMatchType,
							findParent("RULES").lastIgnoreCase));
					}
					else
					{
						rules.addRule(ParserRule.createRegexpEOLSpanRule(
							tag.lastHashChar,tag.lastStartPosMatch,
							tag.lastStart.toString(),tag.lastDelegateSet,
							tag.lastTokenID,tag.lastMatchType,
							findParent("RULES").lastIgnoreCase));
					}
				}
				catch(PatternSyntaxException re)
				{
					error("regexp",re);
				}
			} //}}}
			//{{{ MARK_FOLLOWING
			else if (tag.tagName.equals("MARK_FOLLOWING"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","MARK_FOLLOWING");
					return;
				}

				rules.addRule(ParserRule
					.createMarkFollowingRule(
					tag.lastStartPosMatch,tag.lastStart.toString(),
					tag.lastTokenID,tag.lastMatchType));
			} //}}}
			//{{{ MARK_PREVIOUS
			else if (tag.tagName.equals("MARK_PREVIOUS"))
			{
				if(tag.lastStart == null)
				{
					error("empty-tag","MARK_PREVIOUS");
					return;
				}

				rules.addRule(ParserRule
					.createMarkPreviousRule(
					tag.lastStartPosMatch,tag.lastStart.toString(),
					tag.lastTokenID,tag.lastMatchType));
			} //}}}
			//{{{ Keywords
			else if (
				!tag.tagName.equals("END")
				&& !tag.tagName.equals("BEGIN")
				&& !tag.tagName.equals("KEYWORDS")
				&& !tag.tagName.equals("MODE")
			) {
				byte token = Token.stringToToken(tag.tagName);
				if(token != -1)
					addKeyword(tag.lastKeyword.toString(),token);
			} //}}}
		}
		else
		{
			// can't happen
			throw new InternalError();
		}
	} //}}}

	//{{{ startDocument() method
	public void startDocument()
	{
		props = new Hashtable();
		pushElement(null, null);
		reloadModes = new Vector();
	} //}}}

	//{{{ endDocument() method
	public void endDocument()
	{
		ParserRuleSet[] rulesets = marker.getRuleSets();
		for(int i = 0; i < rulesets.length; i++)
		{
			rulesets[i].resolveImports();
		}
		for(Mode mode : reloadModes)
		{
			mode.setTokenMarker(null);
			mode.loadIfNecessary();
		}
	} //}}}

	//{{{ getTokenMarker() method
	/**
	 * Returns the TokenMarker.
	 *
	 * @return a TokenMarker it cannot be null
	 */
	public TokenMarker getTokenMarker()
	{
		return marker;
	} //}}}

	//{{{ getModeProperties() method
	public Hashtable getModeProperties()
	{
		return modeProps;
	} //}}}

	//{{{ Protected members

	//{{{ error() method
	/**
	 * Reports an error.
	 * You must override this method so that the mode loader can do error
	 * reporting.
	 * @param msg The error type
	 * @param subst A String or a Throwable
	 * containing specific information
	 * @since jEdit 4.2pre1
	 */
	protected abstract void error(String msg, Object subst);
	//}}}

	//{{{ getTokenMarker() method
	/**
	 * Returns the token marker for the given mode.
	 * You must override this method so that the mode loader can resolve
	 * delegate targets.
	 * @param mode The mode name
	 * @since jEdit 4.2pre1
	 */
	protected abstract TokenMarker getTokenMarker(String mode);
	//}}}

	//}}}

	//{{{ Private members

	//{{{ Instance variables
	private String modeName;
	/** The token marker cannot be null. */
	private final TokenMarker marker;
	private KeywordMap keywords;
	/** this stack can contains null elements. */
	private Stack stateStack;
	private String propName;
	private String propValue;
	private Hashtable props;
	private Hashtable modeProps;
	private ParserRuleSet rules;
	/**
	 *  A list of modes to be reloaded at the end, loaded through DELEGATEs
	 *  @see http://sourceforge.net/tracker/index.php?func=detail&aid=1742250&group_id=588&atid=100588
	 */
	private Vector reloadModes;
	//}}}

	//{{{ addKeyword() method
	private void addKeyword(String k, byte id)
	{
		if(k == null)
		{
			error("empty-keyword",null);
			return;
		}

		if (keywords == null) return;
		keywords.add(k,id);
	} //}}}

	//{{{ pushElement() method
	private TagDecl pushElement(String name, Attributes attrs)
	{
		if (name != null)
		{
			TagDecl tag = new TagDecl(name, attrs);
			stateStack.push(tag);
			return tag;
		}
		else
		{
			stateStack.push(null);
			return null;
		}
	} //}}}

	//{{{ peekElement() method
	private TagDecl peekElement()
	{
		return stateStack.peek();
	} //}}}

	//{{{ popElement() method
	private TagDecl popElement()
	{
		return stateStack.pop();
	} //}}}

	//{{{ findParent() method
	/**
	 * Finds the first element whose tag matches 'tagName',
	 * searching backwards in the stack.
	 */
	private TagDecl findParent(String tagName)
	{
		for (int idx = stateStack.size() - 1; idx >= 0; idx--)
		{
			TagDecl tag = stateStack.get(idx);
			if (tag.tagName.equals(tagName))
				return tag;
		}
		return null;
	} //}}}

	//}}}

	/**
	 * Hold info about what tag was read and what attributes were
	 * set in the XML file, to be kept by the handler in a stack
	 * (similar to the way tag names were kept in a stack before).
	 */
	private class TagDecl
	{

		public TagDecl(String tagName, Attributes attrs)
		{
			this.tagName = tagName;

			String tmp;

			propName = attrs.getValue("NAME");
			propValue = attrs.getValue("VALUE");

			tmp = attrs.getValue("TYPE");
			if (tmp != null)
			{
				lastTokenID = Token.stringToToken(tmp);
				if(lastTokenID == -1)
					error("token-invalid",tmp);
			}

			lastMatchType = ParserRule.MATCH_TYPE_RULE;
			// check for the deprecated "EXCLUDE_MATCH" and
			// warn if found.
			tmp = attrs.getValue("EXCLUDE_MATCH");
			if (tmp != null)
			{
				Log.log(Log.WARNING, this, modeName + ": EXCLUDE_MATCH is deprecated");
				if ("TRUE".equalsIgnoreCase(tmp))
				{
					lastMatchType = ParserRule.MATCH_TYPE_CONTEXT;
				}
			}

			// override with the newer MATCH_TYPE if present
			tmp = attrs.getValue("MATCH_TYPE");
			if (tmp != null)
			{
				if ("CONTEXT".equals(tmp))
				{
					lastMatchType = ParserRule.MATCH_TYPE_CONTEXT;
				}
				else if ("RULE".equals(tmp))
				{
					lastMatchType = ParserRule.MATCH_TYPE_RULE;
				}
				else
				{
					lastMatchType = Token.stringToToken(tmp);
					if(lastMatchType == -1)
						error("token-invalid",tmp);
				}
			}

			lastAtLineStart = "TRUE".equals(attrs.getValue("AT_LINE_START"));
			lastAtWhitespaceEnd = "TRUE".equals(attrs.getValue("AT_WHITESPACE_END"));
			lastAtWordStart = "TRUE".equals(attrs.getValue("AT_WORD_START"));
			lastNoLineBreak = "TRUE".equals(attrs.getValue("NO_LINE_BREAK"));
			lastNoWordBreak = "TRUE".equals(attrs.getValue("NO_WORD_BREAK"));
			lastIgnoreCase = (attrs.getValue("IGNORE_CASE") == null ||
					"TRUE".equals(attrs.getValue("IGNORE_CASE")));
			lastHighlightDigits = "TRUE".equals(attrs.getValue("HIGHLIGHT_DIGITS"));;
			lastDigitRE = attrs.getValue("DIGIT_RE");

			tmp = attrs.getValue("NO_WORD_SEP");
			if (tmp != null)
				lastNoWordSep = tmp;

			tmp = attrs.getValue("AT_CHAR");
			if (tmp != null)
			{
				try
				{
					termChar = Integer.parseInt(tmp);
				}
				catch (NumberFormatException e)
				{
					error("termchar-invalid",tmp);
					termChar = -1;
				}
			}

			lastEscape = attrs.getValue("ESCAPE");
			lastSetName = attrs.getValue("SET");

			tmp = attrs.getValue("DELEGATE");
			if (tmp != null)
			{
				String delegateMode, delegateSetName;

				int index = tmp.indexOf("::");

				if(index != -1)
				{
					delegateMode = tmp.substring(0,index);
					delegateSetName = tmp.substring(index + 2);
				}
				else
				{
					delegateMode = modeName;
					delegateSetName = tmp;
				}

				TokenMarker delegateMarker = getTokenMarker(delegateMode);
				if(delegateMarker == null)
					error("delegate-invalid",tmp);
				else
				{
					lastDelegateSet = delegateMarker
						.getRuleSet(delegateSetName);
					if(delegateMarker == marker
						&& lastDelegateSet == null)
					{
						// stupid hack to handle referencing
						// a rule set that is defined later!
						lastDelegateSet = new ParserRuleSet(
							delegateMode,
							delegateSetName);
						lastDelegateSet.setDefault(Token.INVALID);
						marker.addRuleSet(lastDelegateSet);
					}
					else if(lastDelegateSet == null)
						error("delegate-invalid",tmp);
				}
			}

			tmp = attrs.getValue("DEFAULT");
			if (tmp != null)
			{
				lastDefaultID = Token.stringToToken(tmp);
				if(lastDefaultID == -1)
				{
					error("token-invalid",tmp);
					lastDefaultID = Token.NULL;
				}
			}

			lastHashChar = attrs.getValue("HASH_CHAR");
			lastHashChars = attrs.getValue("HASH_CHARS");
			if ((null != lastHashChar) && (null != lastHashChars))
			{
				error("hash-char-and-hash-chars-mutually-exclusive",null);
				lastHashChars = null;
			}
		}

		public void setText(char[] c, int off, int len)
		{
			if (tagName.equals("EOL_SPAN") ||
				tagName.equals("EOL_SPAN_REGEXP") ||
				tagName.equals("MARK_PREVIOUS") ||
				tagName.equals("MARK_FOLLOWING") ||
				tagName.equals("SEQ") ||
				tagName.equals("SEQ_REGEXP") ||
				tagName.equals("BEGIN")
			)
			{
				TagDecl target = this;
				if (tagName.equals("BEGIN"))
					target = stateStack.get(stateStack.size() - 2);

				if (target.lastStart == null)
				{
					target.lastStart = new StringBuffer();
					target.lastStart.append(c, off, len);
					target.lastStartPosMatch = ((target.lastAtLineStart ? ParserRule.AT_LINE_START : 0)
						| (target.lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0)
						| (target.lastAtWordStart ? ParserRule.AT_WORD_START : 0));
					target.lastAtLineStart = false;
					target.lastAtWordStart = false;
					target.lastAtWhitespaceEnd = false;
				}
				else
				{
					target.lastStart.append(c, off, len);
				}
			}
			else if (tagName.equals("END"))
			{
				TagDecl target = stateStack.get(stateStack.size() - 2);
				if (target.lastEnd == null)
				{
					target.lastEnd = new StringBuffer();
					target.lastEnd.append(c, off, len);
					target.lastEndPosMatch = ((this.lastAtLineStart ? ParserRule.AT_LINE_START : 0)
						| (this.lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0)
						| (this.lastAtWordStart ? ParserRule.AT_WORD_START : 0));
					target.lastAtLineStart = false;
					target.lastAtWordStart = false;
					target.lastAtWhitespaceEnd = false;
				}
				else
				{
					target.lastEnd.append(c, off, len);
				}
			}
			else
			{
				if (lastKeyword == null)
					lastKeyword = new StringBuffer();
				lastKeyword.append(c, off, len);
			}
		}

		public String tagName;
		public StringBuffer lastStart;
		public StringBuffer lastEnd;
		public StringBuffer lastKeyword;
		public String lastSetName;
		public String lastEscape;
		public ParserRuleSet lastDelegateSet;
		public String lastNoWordSep = "_";
		public ParserRuleSet rules;
		public byte lastDefaultID = Token.NULL;
		public byte lastTokenID;
		public byte lastMatchType;
		public int termChar = -1;
		public boolean lastNoLineBreak;
		public boolean lastNoWordBreak;
		public boolean lastIgnoreCase = true;
		public boolean lastHighlightDigits;
		public boolean lastAtLineStart;
		public boolean lastAtWhitespaceEnd;
		public boolean lastAtWordStart;
		public int lastStartPosMatch;
		public int lastEndPosMatch;
		public String lastDigitRE;
		public String lastHashChar;
		public String lastHashChars;
	}
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy