XmlToAppData.java :  » Database-ORM » Torque » org » apache » torque » engine » database » transform » Java Open Source

Java Open Source » Database ORM » Torque 
Torque » org » apache » torque » engine » database » transform » XmlToAppData.java
package org.apache.torque.engine.database.transform;

/*
 * 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.
 */

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Stack;
import java.util.Vector;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.torque.engine.EngineException;
import org.apache.torque.engine.database.model.Column;
import org.apache.torque.engine.database.model.Database;
import org.apache.torque.engine.database.model.Domain;
import org.apache.torque.engine.database.model.ForeignKey;
import org.apache.torque.engine.database.model.Index;
import org.apache.torque.engine.database.model.Table;
import org.apache.torque.engine.database.model.Unique;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * A Class that is used to parse an input xml schema file and creates a Database
 * java structure.
 *
 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
 * @author <a href="mailto:fischer@seitenbau.de">Thomas Fischer</a>
 * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
 * @version $Id: XmlToAppData.java 473814 2006-11-11 22:30:30Z tv $
 */
public class XmlToAppData extends DefaultHandler
{
    /** Logging class from commons.logging */
    private static Log log = LogFactory.getLog(XmlToAppData.class);

    private Database database;
    private Table currTable;
    private Column currColumn;
    private ForeignKey currFK;
    private Index currIndex;
    private Unique currUnique;

    private boolean firstPass;
    private boolean isExternalSchema;
    private String currentPackage;
    private String currentXmlFile;
    private String defaultPackage;

    private static SAXParserFactory saxFactory;

    /** remember all files we have already parsed to detect looping. */
    private Vector alreadyReadFiles;

    /** this is the stack to store parsing data */
    private Stack parsingStack = new Stack();

    static
    {
        saxFactory = SAXParserFactory.newInstance();
        saxFactory.setValidating(true);
    }

    /**
     * Creates a new instance for the specified database type.
     *
     * @param databaseType The type of database for the application.
     */
    public XmlToAppData(String databaseType)
    {
        database = new Database(databaseType);
        firstPass = true;
    }

    /**
     * Creates a new instance for the specified database type.
     *
     * @param databaseType The type of database for the application.
     * @param defaultPackage the default java package used for the om
     */
    public XmlToAppData(String databaseType, String defaultPackage)
    {
        database = new Database(databaseType);
        this.defaultPackage = defaultPackage;
        firstPass = true;
    }

    /**
     * Parses a XML input file and returns a newly created and
     * populated Database structure.
     *
     * @param xmlFile The input file to parse.
     * @return Database populated by <code>xmlFile</code>.
     */
    public Database parseFile(String xmlFile)
            throws EngineException
    {
        try
        {
            // in case I am missing something, make it obvious
            if (!firstPass)
            {
                throw new Error("No more double pass");
            }
            // check to see if we alread have parsed the file
            if ((alreadyReadFiles != null)
                    && alreadyReadFiles.contains(xmlFile))
            {
                return database;
            }
            else if (alreadyReadFiles == null)
            {
                alreadyReadFiles = new Vector(3, 1);
            }

            // remember the file to avoid looping
            alreadyReadFiles.add(xmlFile);

            currentXmlFile = xmlFile;

            SAXParser parser = saxFactory.newSAXParser();

            FileInputStream fileInputStream = null;
            try
            {
                fileInputStream = new FileInputStream(xmlFile);
            }
            catch (FileNotFoundException fnfe)
            {
                throw new FileNotFoundException
                    (new File(xmlFile).getAbsolutePath());
            }
            BufferedInputStream bufferedInputStream
                    = new BufferedInputStream(fileInputStream);
            try
            {
                log.info("Parsing file: '"
                        + (new File(xmlFile)).getName() + "'");
                InputSource is = new InputSource(bufferedInputStream);
                parser.parse(is, this);
            }
            finally
            {
                bufferedInputStream.close();
            }
        }
        catch (SAXParseException e)
        {
            throw new EngineException("Sax error on line "
                        + e.getLineNumber()
                        + " column "
                        + e.getColumnNumber()
                        + " : "
                        + e.getMessage(),
                    e);
        }
        catch (Exception e)
        {
            throw new EngineException(e);
        }
        if (!isExternalSchema)
        {
            firstPass = false;
        }
        database.doFinalInitialization();
        return database;
    }

    /**
     * EntityResolver implementation. Called by the XML parser
     *
     * @param publicId The public identifier of the external entity
     * @param systemId The system identifier of the external entity
     * @return an InputSource for the database.dtd file
     * @see DTDResolver#resolveEntity(String, String)
     */
    public InputSource resolveEntity(String publicId, String systemId)
            throws SAXException
    {
        try
        {
            return new DTDResolver().resolveEntity(publicId, systemId);
        }
        catch (Exception e)
        {
            throw new SAXException(e);
        }
    }

    /**
     * Handles opening elements of the xml file.
     *
     * @param uri
     * @param localName The local name (without prefix), or the empty string if
     *         Namespace processing is not being performed.
     * @param rawName The qualified name (with prefix), or the empty string if
     *         qualified names are not available.
     * @param attributes The specified or defaulted attributes
     */
    public void startElement(String uri, String localName, String rawName,
                             Attributes attributes)
            throws SAXException
    {
        try
        {
            if (rawName.equals("database"))
            {
                if (isExternalSchema)
                {
                    currentPackage = attributes.getValue("package");
                    if (currentPackage == null)
                    {
                        currentPackage = defaultPackage;
                    }
                }
                else
                {
                    database.loadFromXML(attributes);
                    if (database.getPackage() == null)
                    {
                        database.setPackage(defaultPackage);
                    }
                }
            }
            else if (rawName.equals("external-schema"))
            {
                String xmlFile = attributes.getValue("filename");
                if (xmlFile.charAt(0) != '/')
                {
                    File f = new File(currentXmlFile);
                    xmlFile = new File(f.getParent(), xmlFile).getPath();
                }

                // put current state onto the stack
                ParseStackElement.pushState(this);

                isExternalSchema = true;

                parseFile(xmlFile);
                // get the last state from the stack
                ParseStackElement.popState(this);
            }
            else if (rawName.equals("domain"))
            {
                Domain domain = new Domain();
                domain.loadFromXML(attributes, database.getPlatform());
                database.addDomain(domain);
            }
            else if (rawName.equals("table"))
            {
                currTable = database.addTable(attributes);
                if (isExternalSchema)
                {
                    currTable.setForReferenceOnly(true);
                    currTable.setPackage(currentPackage);
                }
            }
            else if (rawName.equals("column"))
            {
                currColumn = currTable.addColumn(attributes);
            }
            else if (rawName.equals("inheritance"))
            {
                currColumn.addInheritance(attributes);
            }
            else if (rawName.equals("foreign-key"))
            {
                currFK = currTable.addForeignKey(attributes);
            }
            else if (rawName.equals("reference"))
            {
                currFK.addReference(attributes);
            }
            else if (rawName.equals("index"))
            {
                currIndex = currTable.addIndex(attributes);
            }
            else if (rawName.equals("index-column"))
            {
                currIndex.addColumn(attributes);
            }
            else if (rawName.equals("unique"))
            {
                currUnique = currTable.addUnique(attributes);
            }
            else if (rawName.equals("unique-column"))
            {
                currUnique.addColumn(attributes);
            }
            else if (rawName.equals("id-method-parameter"))
            {
                currTable.addIdMethodParameter(attributes);
            }
            else if (rawName.equals("option"))
            {
                setOption(attributes);
            }
        }
        catch (Exception e)
        {
            throw new SAXException(e);
        }
    }

    /**
     * Handles closing elements of the xml file.
     *
     * @param uri
     * @param localName The local name (without prefix), or the empty string if
     *         Namespace processing is not being performed.
     * @param rawName The qualified name (with prefix), or the empty string if
     *         qualified names are not available.
     */
    public void endElement(String uri, String localName, String rawName)
        throws SAXException
    {
        if (log.isDebugEnabled())
        {
            log.debug("endElement(" + uri + ", " + localName + ", "
                    + rawName + ") called");
        }
        try
        {
            // Reset working objects to null to allow option to know
            // which element it is associated with.
            if (rawName.equals("table"))
            {
                currTable = null;
            }
            else if (rawName.equals("column"))
            {
                currColumn = null;
            }
            else if (rawName.equals("foreign-key"))
            {
                currFK = null;
            }
            else if (rawName.equals("index"))
            {
                currIndex = null;
            }
            else if (rawName.equals("unique"))
            {
                currUnique = null;
            }
        }
        catch (Exception e)
        {
            throw new SAXException(e);
        }
    }

    public void setOption(Attributes attributes)
    {
        // Look thru supported model elements in reverse order to
        // find one that this option statement applies to.

        String key = attributes.getValue("key");
        String value = attributes.getValue("value");
        if (currUnique != null)
        {
            currUnique.addOption(key, value);
        }
        else if (currIndex != null)
        {
            currIndex.addOption(key, value);
        }
        else if (currFK != null)
        {
            currFK.addOption(key, value);
        }
        else if (currColumn != null)
        {
            currColumn.addOption(key, value);
        }
        else if (currTable != null)
        {
            currTable.addOption(key, value);
        }
        else
        {                            // Must be a db level option.
            database.addOption(key, value);
        }
    }

    /**
     * Handles exception which occur when the xml file is parsed
     * @param e the exception which occured while parsing
     * @throws SAXException always
     */
    public void error(SAXParseException e) throws SAXException
    {
        log.error("Sax parser threw an Exception", e);
        throw new SAXException(
                "Error while parsing "
                + currentXmlFile
                + " at line "
                + e.getLineNumber()
                + " column "
                + e.getColumnNumber()
                + " : "
                + e.getMessage());
    }

    /**
     * When parsing multiple files that use nested <external-schema> tags we
     * need to use a stack to remember some values.
     */
    private static class ParseStackElement
    {
        private boolean isExternalSchema;
        private String currentPackage;
        private String currentXmlFile;
        private boolean firstPass;

        /**
         *
         * @param parser
         */
        public ParseStackElement(XmlToAppData parser)
        {
            // remember current state of parent object
            isExternalSchema = parser.isExternalSchema;
            currentPackage = parser.currentPackage;
            currentXmlFile = parser.currentXmlFile;
            firstPass = parser.firstPass;

            // push the state onto the stack
            parser.parsingStack.push(this);
        }

        /**
         * Removes the top element from the stack and activates the stored state
         *
         * @param parser
         */
        public static void popState(XmlToAppData parser)
        {
            if (!parser.parsingStack.isEmpty())
            {
                ParseStackElement elem = (ParseStackElement)
                        parser.parsingStack.pop();

                // activate stored state
                parser.isExternalSchema = elem.isExternalSchema;
                parser.currentPackage = elem.currentPackage;
                parser.currentXmlFile = elem.currentXmlFile;
                parser.firstPass = elem.firstPass;
            }
        }

        /**
         * Stores the current state on the top of the stack.
         *
         * @param parser
         */
        public static void pushState(XmlToAppData parser)
        {
            new ParseStackElement(parser);
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.