PageRenderTime 41ms CodeModel.GetById 14ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/syntax/XModeHandler.java

#
Java | 711 lines | 553 code | 58 blank | 100 comment | 180 complexity | 689cd4d0fd0cbfc15eb66957beb6b1a1 MD5 | raw file
  1/*
  2 * XModeHandler.java - XML handler for mode files
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999 mike dillon
  7 * Portions copyright (C) 2000, 2001 Slava Pestov
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or any later version.
 13 *
 14 * This program is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22 */
 23
 24package org.gjt.sp.jedit.syntax;
 25
 26//{{{ Imports
 27import com.microstar.xml.*;
 28import gnu.regexp.*;
 29import java.io.*;
 30import java.util.*;
 31import org.gjt.sp.util.Log;
 32//}}}
 33
 34/**
 35 * XML handler for mode definition files.
 36 */
 37public abstract class XModeHandler extends HandlerBase
 38{
 39	//{{{ XModeHandler constructor
 40	public XModeHandler (String modeName)
 41	{
 42		this.modeName = modeName;
 43		marker = new TokenMarker();
 44		marker.addRuleSet(new ParserRuleSet(modeName,"MAIN"));
 45		stateStack = new Stack();
 46
 47		// default value
 48		lastNoWordSep = "_";
 49	} //}}}
 50
 51	//{{{ resolveEntity() method
 52	public Object resolveEntity(String publicId, String systemId)
 53	{
 54		if("xmode.dtd".equals(systemId))
 55		{
 56			// this will result in a slight speed up, since we
 57			// don't need to read the DTD anyway, as AElfred is
 58			// non-validating
 59			return new StringReader("<!-- -->");
 60
 61			/* try
 62			{
 63				return new BufferedReader(new InputStreamReader(
 64					getClass().getResourceAsStream(
 65					"/org/gjt/sp/jedit/syntax/xmode.dtd")));
 66			}
 67			catch(Exception e)
 68			{
 69				error("dtd",e);
 70			} */
 71		}
 72
 73		return null;
 74	} //}}}
 75
 76	//{{{ attribute() method
 77	public void attribute(String aname, String value, boolean isSpecified)
 78	{
 79		aname = (aname == null) ? null : aname.intern();
 80
 81		if (aname == "NAME")
 82		{
 83			propName = value;
 84		}
 85		else if (aname == "VALUE")
 86		{
 87			propValue = value;
 88		}
 89		else if (aname == "TYPE")
 90		{
 91			lastTokenID = Token.stringToToken(value);
 92			if(lastTokenID == -1)
 93				error("token-invalid",value);
 94		}
 95		else if (aname == "AT_LINE_START")
 96		{
 97			lastAtLineStart = (isSpecified) ? (value.equals("TRUE")) :
 98				false;
 99		}
100		else if (aname == "AT_WHITESPACE_END")
101		{
102			lastAtWhitespaceEnd = (isSpecified) ? (value.equals("TRUE")) :
103				false;
104		}
105		else if (aname == "AT_WORD_START")
106		{
107			lastAtWordStart = (isSpecified) ? (value.equals("TRUE")) :
108				false;
109		}
110		else if (aname == "NO_LINE_BREAK")
111		{
112			lastNoLineBreak = (isSpecified) ? (value.equals("TRUE")) :
113				false;
114		}
115		else if (aname == "NO_WORD_BREAK")
116		{
117			lastNoWordBreak = (isSpecified) ? (value.equals("TRUE")) :
118				false;
119		}
120		else if (aname == "NO_ESCAPE")
121		{
122			lastNoEscape = (isSpecified) ? (value.equals("TRUE")) :
123				false;
124		}
125		else if (aname == "EXCLUDE_MATCH")
126		{
127			lastExcludeMatch = (isSpecified) ? (value.equals("TRUE")) :
128				false;
129		}
130		else if (aname == "IGNORE_CASE")
131		{
132			lastIgnoreCase = (isSpecified) ? (value.equals("TRUE")) :
133				true;
134		}
135		else if (aname == "HIGHLIGHT_DIGITS")
136		{
137			lastHighlightDigits = (isSpecified) ? (value.equals("TRUE")) :
138				false;
139		}
140		else if (aname == "DIGIT_RE")
141		{
142			lastDigitRE = value;
143		}
144		else if (aname == "NO_WORD_SEP")
145		{
146			if(isSpecified)
147				lastNoWordSep = value;
148		}
149		else if (aname == "AT_CHAR")
150		{
151			try
152			{
153				if (isSpecified) termChar =
154					Integer.parseInt(value);
155			}
156			catch (NumberFormatException e)
157			{
158				error("termchar-invalid",value);
159				termChar = -1;
160			}
161		}
162		else if (aname == "ESCAPE")
163		{
164			lastEscape = value;
165		}
166		else if (aname == "SET")
167		{
168			lastSetName = value;
169		}
170		else if (aname == "DELEGATE")
171		{
172			String delegateMode, delegateSetName;
173
174			if(value != null)
175			{
176				int index = value.indexOf("::");
177
178				if(index != -1)
179				{
180					delegateMode = value.substring(0,index);
181					delegateSetName = value.substring(index + 2);
182				}
183				else
184				{
185					delegateMode = modeName;
186					delegateSetName = value;
187				}
188
189				TokenMarker delegateMarker = getTokenMarker(delegateMode);
190				if(delegateMarker == null)
191					error("delegate-invalid",value);
192				else
193				{
194					lastDelegateSet = delegateMarker
195						.getRuleSet(delegateSetName);
196					if(delegateMarker == marker
197						&& lastDelegateSet == null)
198					{
199						// stupid hack to handle referencing
200						// a rule set that is defined later!
201						lastDelegateSet = new ParserRuleSet(
202							delegateMode,
203							delegateSetName);
204						lastDelegateSet.setDefault(Token.INVALID);
205						marker.addRuleSet(lastDelegateSet);
206					}
207					else if(lastDelegateSet == null)
208						error("delegate-invalid",value);
209				}
210			}
211		}
212		else if (aname == "DEFAULT")
213		{
214			lastDefaultID = Token.stringToToken(value);
215			if(lastDefaultID == -1)
216			{
217				error("token-invalid",value);
218				lastDefaultID = Token.NULL;
219			}
220		}
221		else if (aname == "HASH_CHAR")
222		{
223			if(value.length() != 1)
224			{
225				error("hash-char-invalid",value);
226				lastDefaultID = Token.NULL;
227			}
228			else
229				lastHashChar = value.charAt(0);
230		}
231	} //}}}
232
233	//{{{ doctypeDecl() method
234	public void doctypeDecl(String name, String publicId,
235		String systemId) throws Exception
236	{
237		if ("MODE".equalsIgnoreCase(name)) return;
238
239		error("doctype-invalid",name);
240	} //}}}
241
242	//{{{ charData() method
243	public void charData(char[] c, int off, int len)
244	{
245		String tag = peekElement();
246		String text = new String(c, off, len);
247
248		if (tag == "EOL_SPAN" ||
249			tag == "EOL_SPAN_REGEXP" ||
250			tag == "MARK_PREVIOUS" ||
251			tag == "MARK_FOLLOWING" ||
252			tag == "SEQ" ||
253			tag == "SEQ_REGEXP" ||
254			tag == "BEGIN"
255		)
256		{
257			lastStart = text;
258			lastStartPosMatch = ((lastAtLineStart ? ParserRule.AT_LINE_START : 0)
259				| (lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0)
260				| (lastAtWordStart ? ParserRule.AT_WORD_START : 0));
261			lastAtLineStart = false;
262			lastAtWordStart = false;
263			lastAtWhitespaceEnd = false;
264		}
265		else if (tag == "END")
266		{
267			lastEnd = text;
268			lastEndPosMatch = ((lastAtLineStart ? ParserRule.AT_LINE_START : 0)
269				| (lastAtWhitespaceEnd ? ParserRule.AT_WHITESPACE_END : 0)
270				| (lastAtWordStart ? ParserRule.AT_WORD_START : 0));
271			lastAtLineStart = false;
272			lastAtWordStart = false;
273			lastAtWhitespaceEnd = false;
274		}
275		else
276		{
277			lastKeyword = text;
278		}
279	} //}}}
280
281	//{{{ startElement() method
282	public void startElement (String tag)
283	{
284		tag = pushElement(tag);
285
286		if (tag == "WHITESPACE")
287		{
288			Log.log(Log.WARNING,this,modeName + ": WHITESPACE rule "
289				+ "no longer needed");
290		}
291		else if (tag == "KEYWORDS")
292		{
293			keywords = new KeywordMap(rules.getIgnoreCase());
294		}
295		else if (tag == "RULES")
296		{
297			if(lastSetName == null)
298				lastSetName = "MAIN";
299			rules = marker.getRuleSet(lastSetName);
300			if(rules == null)
301			{
302				rules = new ParserRuleSet(modeName,lastSetName);
303				marker.addRuleSet(rules);
304			}
305			rules.setIgnoreCase(lastIgnoreCase);
306			rules.setHighlightDigits(lastHighlightDigits);
307			if(lastDigitRE != null)
308			{
309				try
310				{
311					rules.setDigitRegexp(new RE(lastDigitRE,
312						lastIgnoreCase
313						? RE.REG_ICASE : 0,
314						ParserRule.RE_SYNTAX_JEDIT));
315				}
316				catch(REException e)
317				{
318					error("regexp",e);
319				}
320			}
321
322			if(lastEscape != null)
323				rules.setEscapeRule(ParserRule.createEscapeRule(lastEscape));
324			rules.setDefault(lastDefaultID);
325			rules.setNoWordSep(lastNoWordSep);
326		}
327	} //}}}
328
329	//{{{ endElement() method
330	public void endElement (String name)
331	{
332		if (name == null) return;
333
334		String tag = popElement();
335
336		if (name.equals(tag))
337		{
338			//{{{ PROPERTY
339			if (tag == "PROPERTY")
340			{
341				props.put(propName,propValue);
342			} //}}}
343			//{{{ PROPS
344			else if (tag == "PROPS")
345			{
346				if(peekElement().equals("RULES"))
347					rules.setProperties(props);
348				else
349					modeProps = props;
350
351				props = new Hashtable();
352			} //}}}
353			//{{{ RULES
354			else if (tag == "RULES")
355			{
356				rules.setKeywords(keywords);
357				keywords = null;
358				lastSetName = null;
359				lastEscape = null;
360				lastIgnoreCase = true;
361				lastHighlightDigits = false;
362				lastDigitRE = null;
363				lastDefaultID = Token.NULL;
364				lastNoWordSep = "_";
365				rules = null;
366			} //}}}
367			//{{{ IMPORT
368			else if (tag == "IMPORT")
369			{
370				rules.addRuleSet(lastDelegateSet);
371				lastDelegateSet = null;
372			} //}}}
373			//{{{ TERMINATE
374			else if (tag == "TERMINATE")
375			{
376				rules.setTerminateChar(termChar);
377				termChar = -1;
378			} //}}}
379			//{{{ SEQ
380			else if (tag == "SEQ")
381			{
382				if(lastStart == null)
383				{
384					error("empty-tag","SEQ");
385					return;
386				}
387
388				rules.addRule(ParserRule.createSequenceRule(
389					lastStartPosMatch,lastStart,lastDelegateSet,
390					lastTokenID));
391				reset();
392			} //}}}
393			//{{{ SEQ_REGEXP
394			else if (tag == "SEQ_REGEXP")
395			{
396				if(lastStart == null)
397				{
398					error("empty-tag","SEQ_REGEXP");
399					return;
400				}
401
402				try
403				{
404					rules.addRule(ParserRule.createRegexpSequenceRule(
405						lastHashChar,lastStartPosMatch,
406						lastStart,lastDelegateSet,lastTokenID,
407						lastIgnoreCase));
408				}
409				catch(REException re)
410				{
411					error("regexp",re);
412				}
413
414				reset();
415			} //}}}
416			//{{{ SPAN
417			else if (tag == "SPAN")
418			{
419				if(lastStart == null)
420				{
421					error("empty-tag","BEGIN");
422					return;
423				}
424
425				if(lastEnd == null)
426				{
427					error("empty-tag","END");
428					return;
429				}
430
431				rules.addRule(ParserRule
432					.createSpanRule(
433					lastStartPosMatch,lastStart,
434					lastEndPosMatch,lastEnd,
435					lastDelegateSet,
436					lastTokenID,lastExcludeMatch,
437					lastNoLineBreak,
438					lastNoWordBreak,
439					lastNoEscape));
440
441				reset();
442			} //}}}
443			//{{{ SPAN_REGEXP
444			else if (tag == "SPAN_REGEXP")
445			{
446				if(lastStart == null)
447				{
448					error("empty-tag","BEGIN");
449					return;
450				}
451
452				if(lastEnd == null)
453				{
454					error("empty-tag","END");
455					return;
456				}
457
458				try
459				{
460					rules.addRule(ParserRule
461						.createRegexpSpanRule(
462						lastHashChar,
463						lastStartPosMatch,lastStart,
464						lastEndPosMatch,lastEnd,
465						lastDelegateSet,
466						lastTokenID,
467						lastExcludeMatch,
468						lastNoLineBreak,
469						lastNoWordBreak,
470						lastIgnoreCase,
471						lastNoEscape));
472				}
473				catch(REException re)
474				{
475					error("regexp",re);
476				}
477
478				reset();
479			} //}}}
480			//{{{ EOL_SPAN
481			else if (tag == "EOL_SPAN")
482			{
483				if(lastStart == null)
484				{
485					error("empty-tag","EOL_SPAN");
486					return;
487				}
488
489				rules.addRule(ParserRule.createEOLSpanRule(
490					lastStartPosMatch,lastStart,
491					lastDelegateSet,lastTokenID,
492					lastExcludeMatch));
493
494				reset();
495			} //}}}
496			//{{{ EOL_SPAN_REGEXP
497			else if (tag == "EOL_SPAN_REGEXP")
498			{
499				if(lastStart == null)
500				{
501					error("empty-tag","EOL_SPAN_REGEXP");
502					return;
503				}
504
505				try
506				{
507					rules.addRule(ParserRule.createRegexpEOLSpanRule(
508						lastHashChar,lastStartPosMatch,lastStart,
509						lastDelegateSet,lastTokenID,
510						lastExcludeMatch,lastIgnoreCase));
511				}
512				catch(REException re)
513				{
514					error("regexp",re);
515				}
516
517				reset();
518			} //}}}
519			//{{{ MARK_FOLLOWING
520			else if (tag == "MARK_FOLLOWING")
521			{
522				if(lastStart == null)
523				{
524					error("empty-tag","MARK_FOLLOWING");
525					return;
526				}
527
528				rules.addRule(ParserRule
529					.createMarkFollowingRule(
530					lastStartPosMatch,lastStart,
531					lastTokenID,lastExcludeMatch));
532				reset();
533			} //}}}
534			//{{{ MARK_PREVIOUS
535			else if (tag == "MARK_PREVIOUS")
536			{
537				if(lastStart == null)
538				{
539					error("empty-tag","MARK_PREVIOUS");
540					return;
541				}
542
543				rules.addRule(ParserRule
544					.createMarkPreviousRule(
545					lastStartPosMatch,lastStart,
546					lastTokenID,lastExcludeMatch));
547				reset();
548			} //}}}
549			//{{{ Keywords
550			else
551			{
552				byte token = Token.stringToToken(tag);
553				if(token != -1)
554					addKeyword(lastKeyword,token);
555			} //}}}
556		}
557		else
558		{
559			// can't happen
560			throw new InternalError();
561		}
562	} //}}}
563
564	//{{{ startDocument() method
565	public void startDocument()
566	{
567		props = new Hashtable();
568
569		pushElement(null);
570	} //}}}
571
572	//{{{ endDocument() method
573	public void endDocument()
574	{
575		ParserRuleSet[] rulesets = marker.getRuleSets();
576		for(int i = 0; i < rulesets.length; i++)
577		{
578			rulesets[i].resolveImports();
579		}
580	} //}}}
581
582	//{{{ getTokenMarker() method
583	public TokenMarker getTokenMarker()
584	{
585		return marker;
586	} //}}}
587
588	//{{{ getModeProperties() method
589	public Hashtable getModeProperties()
590	{
591		return modeProps;
592	} //}}}
593
594	//{{{ Protected members
595
596	//{{{ error() method
597	/**
598	 * Reports an error.
599	 * You must override this method so that the mode loader can do error
600	 * reporting.
601	 * @param msg The error type
602	 * @param subst A <code>String</code> or a <code>Throwable</code>
603	 * containing specific information
604	 * @since jEdit 4.2pre1
605	 */
606	protected abstract void error(String msg, Object subst);
607	//}}}
608
609	//{{{ getTokenMarker() method
610	/**
611	 * Returns the token marker for the given mode.
612	 * You must override this method so that the mode loader can resolve
613	 * delegate targets.
614	 * @param mode The mode name
615	 * @since jEdit 4.2pre1
616	 */
617	protected abstract TokenMarker getTokenMarker(String mode);
618	//}}}
619
620	//}}}
621
622	//{{{ Private members
623
624	//{{{ Instance variables
625	private String modeName;
626	private TokenMarker marker;
627	private KeywordMap keywords;
628	private Stack stateStack;
629	private String propName;
630	private String propValue;
631	private Hashtable props;
632	private Hashtable modeProps;
633	private String lastStart;
634	private String lastEnd;
635	private String lastKeyword;
636	private String lastSetName;
637	private String lastEscape;
638	private ParserRuleSet lastDelegateSet;
639	private String lastNoWordSep;
640	private ParserRuleSet rules;
641	private byte lastDefaultID = Token.NULL;
642	private byte lastTokenID;
643	private int termChar = -1;
644	private boolean lastNoLineBreak;
645	private boolean lastNoWordBreak;
646	private boolean lastExcludeMatch;
647	private boolean lastIgnoreCase = true;
648	private boolean lastHighlightDigits;
649	private boolean lastAtLineStart;
650	private boolean lastAtWhitespaceEnd;
651	private boolean lastAtWordStart;
652	private boolean lastNoEscape;
653	private int lastStartPosMatch;
654	private int lastEndPosMatch;
655	private String lastDigitRE;
656	private char lastHashChar;
657	//}}}
658
659	//{{{ reset() method
660	private void reset()
661	{
662		lastHashChar = '\0';
663		lastStartPosMatch = 0;
664		lastStart = null;
665		lastEndPosMatch = 0;
666		lastEnd = null;
667		lastDelegateSet = null;
668		lastTokenID = Token.NULL;
669		lastExcludeMatch = false;
670		lastNoLineBreak = false;
671		lastNoWordBreak = false;
672		lastNoEscape = false;
673	} //}}}
674
675	//{{{ addKeyword() method
676	private void addKeyword(String k, byte id)
677	{
678		if(k == null)
679		{
680			error("empty-keyword",null);
681			return;
682		}
683
684		if (keywords == null) return;
685		keywords.add(k,id);
686	} //}}}
687
688	//{{{ pushElement() method
689	private String pushElement(String name)
690	{
691		name = (name == null) ? null : name.intern();
692
693		stateStack.push(name);
694
695		return name;
696	} //}}}
697
698	//{{{ peekElement() method
699	private String peekElement()
700	{
701		return (String) stateStack.peek();
702	} //}}}
703
704	//{{{ popElement() method
705	private String popElement()
706	{
707		return (String) stateStack.pop();
708	} //}}}
709
710	//}}}
711}