/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

/* $Id: EventModelParser.java 985571 2010-08-14 19:28:26Z jeremias $ */

package org.apache.fop.events.model;

import java.util.Stack;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.util.DefaultErrorListener;

/**
 * This is a parser for the event model XML.
 */
public final class EventModelParser {

    private EventModelParser() {
    }

    /** Logger instance */
    private static final Log LOG = LogFactory.getLog(EventModelParser.class);

    private static SAXTransformerFactory tFactory
        = (SAXTransformerFactory)SAXTransformerFactory.newInstance();

    /**
     * Parses an event model file into an EventModel instance.
     * @param src the Source instance pointing to the XML file
     * @return the created event model structure
     * @throws TransformerException if an error occurs while parsing the XML file
     */
    public static EventModel parse(Source src)
            throws TransformerException {
        Transformer transformer = tFactory.newTransformer();
        transformer.setErrorListener(new DefaultErrorListener(LOG));

        EventModel model = new EventModel();
        SAXResult res = new SAXResult(getContentHandler(model));

        transformer.transform(src, res);
        return model;
    }

    /**
     * Creates a new ContentHandler instance that you can send the event model XML to. The parsed
     * content is accumulated in the model structure.
     * @param model the EventModel
     * @return the ContentHandler instance to receive the SAX stream from the XML file
     */
    public static ContentHandler getContentHandler(EventModel model) {
        return new Handler(model);
    }

    private static class Handler extends DefaultHandler {

        private EventModel model;
        private Stack objectStack = new Stack();

        public Handler(EventModel model) {
            this.model = model;
        }

        /** {@inheritDoc} */
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                    throws SAXException {
            try {
                if ("event-model".equals(localName)) {
                    if (objectStack.size() > 0) {
                        throw new SAXException("event-model must be the root element");
                    }
                    objectStack.push(model);
                } else if ("producer".equals(localName)) {
                    EventProducerModel producer = new EventProducerModel(
                            attributes.getValue("name"));
                    EventModel parent = (EventModel)objectStack.peek();
                    parent.addProducer(producer);
                    objectStack.push(producer);
                } else if ("method".equals(localName)) {
                    EventSeverity severity = EventSeverity.valueOf(attributes.getValue("severity"));
                    String ex = attributes.getValue("exception");
                    EventMethodModel method = new EventMethodModel(
                            attributes.getValue("name"), severity);
                    if (ex != null && ex.length() > 0) {
                        method.setExceptionClass(ex);
                    }
                    EventProducerModel parent = (EventProducerModel)objectStack.peek();
                    parent.addMethod(method);
                    objectStack.push(method);
                } else if ("parameter".equals(localName)) {
                    String className = attributes.getValue("type");
                    Class type;
                    try {
                        type = Class.forName(className);
                    } catch (ClassNotFoundException e) {
                        throw new SAXException("Could not find Class for: " + className, e);
                    }
                    String name = attributes.getValue("name");
                    EventMethodModel parent = (EventMethodModel)objectStack.peek();
                    objectStack.push(parent.addParameter(type, name));
                } else {
                    throw new SAXException("Invalid element: " + qName);
                }
            } catch (ClassCastException cce) {
                throw new SAXException("XML format error: " + qName, cce);
            }
        }

        /** {@inheritDoc} */
        public void endElement(String uri, String localName, String qName) throws SAXException {
            objectStack.pop();
        }

    }

}
