001: /*
002: * Copyright (C) Chaperon. All rights reserved.
003: * -------------------------------------------------------------------------
004: * This software is published under the terms of the Apache Software License
005: * version 1.1, a copy of which has been included with this distribution in
006: * the LICENSE file.
007: */
008:
009: package net.sourceforge.chaperon.model.grammar;
010:
011: import net.sourceforge.chaperon.model.symbol.Nonterminal;
012: import net.sourceforge.chaperon.model.symbol.Symbol;
013: import net.sourceforge.chaperon.model.symbol.Terminal;
014:
015: import org.xml.sax.Attributes;
016: import org.xml.sax.Locator;
017: import org.xml.sax.SAXException;
018: import org.xml.sax.helpers.DefaultHandler;
019:
020: import java.util.Hashtable;
021: import java.util.Stack;
022:
023: /**
024: * This class should generate a grammar from a SAX stream
025: *
026: * @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
027: * @version CVS $Id: GrammarFactory.java,v 1.5 2003/12/14 09:48:34 benedikta Exp $
028: */
029: public class GrammarFactory extends DefaultHandler {
030: /** The namspace of the grammar configuration */
031: public static final String NS = "http://chaperon.sourceforge.net/schema/grammar/1.0";
032:
033: /** Element name */
034: public static final String GRAMMAR_ELEMENT = "grammar";
035:
036: /** Attribute name of the associativity property */
037:
038: // public final static String TOKEN_ASSOCIATIVE_ATTRIBUTE = "assoc";
039: /** Element name */
040: public static final String PRODUCTION_ELEMENT = "production";
041:
042: /** Attribute name of the Precedence property */
043: public static final String PRECEDENCE_ATTRIBUTE = "precedence";
044:
045: /** Element name */
046: public static final String NONTERMINALSYMBOL_ELEMENT = "nonterminal";
047:
048: /** Element name */
049: public static final String TERMINALSYMBOL_ELEMENT = "terminal";
050:
051: /** Element name */
052: public static final String ERRORSYMBOL_ELEMENT = "error";
053:
054: /** Element name */
055: public static final String STARTSYMBOL_ELEMENT = "start";
056:
057: /** Element name */
058: public static final String PRIORITY_ELEMENT = "priority";
059:
060: /** Element name */
061: public static final String ASSOCIATIVITY_ELEMENT = "associativity";
062:
063: /** Attribute name of the associativity property */
064: public static final String TYPE_ATTRIBUTE = "type";
065:
066: /** Attribute name */
067: public static final String SYMBOL_ATTRIBUTE = "symbol";
068: private static final int STATE_OUTER = 0;
069: private static final int STATE_GRAMMAR = 1;
070: private static final int STATE_PRODUCTION = 2;
071: private static final int STATE_START = 3;
072: private static final int STATE_NONTERMINAL = 4;
073: private static final int STATE_TERMINAL = 5;
074: private static final int STATE_ASSOCIATIVITY = 6;
075: private static final int STATE_PRIORITY = 7;
076: private static final int STATE_PRIORITYTERMINAL = 8;
077: private static final int STATE_ERROR = 9;
078: private int state = STATE_OUTER;
079: private Grammar grammar;
080: private Hashtable terminals = new Hashtable();
081: private Hashtable nonterminals = new Hashtable();
082: private Stack stack;
083: private Locator locator = null;
084: private int priorities = 0;
085:
086: /**
087: * Returns the generated Grammar
088: *
089: * @return Grammar
090: */
091: public Grammar getGrammar() {
092: return grammar;
093: }
094:
095: private String getLocation() {
096: if (locator == null)
097: return "unknown";
098:
099: return locator.getSystemId() + ":" + locator.getLineNumber()
100: + ":" + locator.getColumnNumber();
101: }
102:
103: /**
104: * Receive an object for locating the origin of SAX document events.
105: */
106: public void setDocumentLocator(Locator locator) {
107: this .locator = locator;
108: }
109:
110: /**
111: * Receive notification of the beginning of a document.
112: */
113: public void startDocument() {
114: stack = new Stack();
115:
116: state = STATE_OUTER;
117: }
118:
119: /**
120: * Return the content of the associatve attribute
121: *
122: * @param namespaceURI
123: * @param localName
124: * @param qName
125: * @param atts Attributes of an element
126: *
127: * @throws SAXException
128: */
129:
130: /*private Associativity getAssociativityFromAttributes(Attributes atts)
131: {
132: String attribute = atts.getValue(TOKEN_ASSOCIATIVE_ATTRIBUTE);
133:
134: if ((attribute != null) && (attribute.length() > 0))
135: return Associativity.valueOf(attribute);
136: return Associativity.NONASSOC;
137: }*/
138:
139: /**
140: * Return the content of the reducetype attribute
141: *
142: * @param atts Attributes of an element
143: *
144: * @return Reducetype attribute
145: */
146:
147: /*private ReduceType getReduceTypeFromAttributes(Attributes atts)
148: {
149: String attribute = atts.getValue(PRODUCTION_REDUCETYPE_ATTRIBUTE);
150:
151: if ((attribute != null) && (attribute.length() > 0))
152: return ReduceType.valueOf(attribute);
153: return ReduceType.NORMAL;
154: }*/
155:
156: /**
157: * Receive notification of the beginning of an element.
158: *
159: * @param namespaceURI The Namespace URI, or the empty string if the element has no Namespace URI
160: * or if Namespace processing is not being performed.
161: * @param localName The local name (without prefix), or the empty string if Namespace processing
162: * is not being performed.
163: * @param qName The raw XML 1.0 name (with prefix), or the empty string if raw names are not
164: * available.
165: * @param atts The attributes attached to the element. If there are no attributes, it shall be an
166: * empty Attributes object.
167: */
168: public void startElement(String namespaceURI, String localName,
169: String qName, Attributes atts) throws SAXException {
170: if (namespaceURI.equals(NS)) {
171: if ((localName.equals(GRAMMAR_ELEMENT))
172: && (state == STATE_OUTER)) {
173: grammar = new Grammar();
174: grammar.setLocation(getLocation());
175: stack.push(grammar);
176:
177: state = STATE_GRAMMAR;
178: } else if ((localName.equals(PRODUCTION_ELEMENT))
179: && (state == STATE_GRAMMAR)) {
180: Production production = new Production(getNonterminal(
181: nonterminals, atts.getValue(SYMBOL_ATTRIBUTE)));
182: production.setLocation(getLocation());
183:
184: String precedencesymbol = atts
185: .getValue(PRECEDENCE_ATTRIBUTE);
186: if ((precedencesymbol != null)
187: && (precedencesymbol.length() > 0))
188: production.setPrecedence(new Terminal(
189: precedencesymbol));
190:
191: stack.push(production);
192:
193: state = STATE_PRODUCTION;
194: } else if ((localName.equals(NONTERMINALSYMBOL_ELEMENT))
195: && (state == STATE_PRODUCTION)) {
196: stack.push(getNonterminal(nonterminals, atts
197: .getValue(SYMBOL_ATTRIBUTE)));
198:
199: state = STATE_NONTERMINAL;
200: } else if ((localName.equals(TERMINALSYMBOL_ELEMENT))
201: && (state == STATE_PRODUCTION)) {
202: stack
203: .push(new Terminal(atts
204: .getValue(SYMBOL_ATTRIBUTE)));
205:
206: state = STATE_TERMINAL;
207: } else if ((localName.equals(ERRORSYMBOL_ELEMENT))
208: && (state == STATE_PRODUCTION)) {
209: stack.push(new Error());
210:
211: state = STATE_ERROR;
212: } else if ((localName.equals(STARTSYMBOL_ELEMENT))
213: && (state == STATE_GRAMMAR)) {
214: stack.push(new Nonterminal(atts
215: .getValue(SYMBOL_ATTRIBUTE)));
216:
217: state = STATE_START;
218: } else if ((localName.equals(ASSOCIATIVITY_ELEMENT))
219: && (state == STATE_GRAMMAR)) {
220: grammar
221: .setAssociativity(getTerminal(terminals, atts
222: .getValue(SYMBOL_ATTRIBUTE)),
223: new Associativity(atts
224: .getValue(TYPE_ATTRIBUTE)));
225:
226: state = STATE_ASSOCIATIVITY;
227: } else if ((localName.equals(PRIORITY_ELEMENT))
228: && (state == STATE_GRAMMAR)) {
229: priorities = 0;
230:
231: state = STATE_PRIORITY;
232: } else if ((localName.equals(TERMINALSYMBOL_ELEMENT))
233: && (state == STATE_PRIORITY)) {
234: stack.push(getTerminal(terminals, atts
235: .getValue(SYMBOL_ATTRIBUTE)));
236:
237: priorities++;
238:
239: state = STATE_PRIORITYTERMINAL;
240: } else
241: throw new SAXException("Unexpected element " + qName
242: + " at " + getLocation());
243: } else
244: throw new SAXException("Unexpected element " + qName
245: + " at " + getLocation());
246: }
247:
248: /**
249: * Receive notification of the end of an element.
250: *
251: * @param namespaceURI The Namespace URI, or the empty string if the element has no Namespace URI
252: * or if Namespace processing is not being performed.
253: * @param localName The local name (without prefix), or the empty string if Namespace processing
254: * is not being performed.
255: * @param qName The raw XML 1.0 name (with prefix), or the empty string if raw names are not
256: * available.
257: *
258: * @throws SAXException
259: */
260: public void endElement(String namespaceURI, String localName,
261: String qName) throws SAXException {
262: if (namespaceURI.equals(NS)) {
263: if ((localName.equals(GRAMMAR_ELEMENT))
264: && (state == STATE_GRAMMAR)) {
265: grammar = (Grammar) stack.pop();
266:
267: state = STATE_OUTER;
268: } else if ((localName.equals(PRODUCTION_ELEMENT))
269: && (state == STATE_PRODUCTION)) {
270: Production production = (Production) stack.pop();
271: Grammar grammar = (Grammar) stack.peek();
272:
273: grammar.addProduction(production);
274:
275: state = STATE_GRAMMAR;
276: } else if ((localName.equals(NONTERMINALSYMBOL_ELEMENT))
277: && (state == STATE_NONTERMINAL)) {
278: Symbol ntsymbol = (Symbol) stack.pop();
279: Production production = (Production) stack.peek();
280:
281: production.getDefinition().addSymbol(ntsymbol);
282:
283: state = STATE_PRODUCTION;
284: } else if ((localName.equals(TERMINALSYMBOL_ELEMENT))
285: && (state == STATE_TERMINAL)) {
286: Symbol tsymbol = (Symbol) stack.pop();
287: Production production = (Production) stack.peek();
288:
289: production.getDefinition().addSymbol(tsymbol);
290:
291: state = STATE_PRODUCTION;
292: } else if ((localName.equals(ERRORSYMBOL_ELEMENT))
293: && (state == STATE_ERROR)) {
294: Symbol error = (Symbol) stack.pop();
295: Production production = (Production) stack.peek();
296:
297: production.getDefinition().addSymbol(error);
298:
299: state = STATE_PRODUCTION;
300: } else if ((localName.equals(STARTSYMBOL_ELEMENT))
301: && (state == STATE_START)) {
302: Nonterminal ssymbol = (Nonterminal) stack.pop();
303: Grammar grammar = (Grammar) stack.peek();
304:
305: grammar.setStartSymbol(ssymbol);
306:
307: state = STATE_GRAMMAR;
308: } else if ((localName.equals(ASSOCIATIVITY_ELEMENT))
309: && (state == STATE_ASSOCIATIVITY))
310: state = STATE_GRAMMAR;
311: else if ((localName.equals(PRIORITY_ELEMENT))
312: && (state == STATE_PRIORITY)) {
313: int i = 0;
314: while (stack.peek() instanceof Terminal) {
315: grammar.setPriority((Terminal) stack.pop(), i + 1);
316: i++;
317: }
318:
319: state = STATE_GRAMMAR;
320: } else if ((localName.equals(TERMINALSYMBOL_ELEMENT))
321: && (state == STATE_PRIORITYTERMINAL))
322: state = STATE_PRIORITY;
323: else
324: throw new SAXException("Unexpected element " + qName
325: + " at " + getLocation());
326: } else
327: throw new SAXException("Unexpected element " + qName
328: + " at " + getLocation());
329: }
330:
331: private Terminal getTerminal(Hashtable terminals, String name) {
332: Terminal terminal = (Terminal) terminals.get(name);
333: if (terminal == null) {
334: terminal = new Terminal(name);
335: terminals.put(name, terminal);
336: }
337:
338: return terminal;
339: }
340:
341: private Nonterminal getNonterminal(Hashtable nonterminals,
342: String name) {
343: Nonterminal nonterminal = (Nonterminal) nonterminals.get(name);
344: if (nonterminal == null) {
345: nonterminal = new Nonterminal(name);
346: nonterminals.put(name, nonterminal);
347: }
348:
349: return nonterminal;
350: }
351: }
|