Coverage Report - org.apache.torque.engine.database.transform.XmlToAppData
 
Classes in this File Line Coverage Branch Coverage Complexity
XmlToAppData
0%
0/130
0%
0/70
5.727
XmlToAppData$ParseStackElement
0%
0/16
0%
0/2
5.727
 
 1  
 package org.apache.torque.engine.database.transform;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 5  
  * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 7  
  * License. You may obtain a copy of the License at
 8  
  * 
 9  
  * http://www.apache.org/licenses/LICENSE-2.0
 10  
  * 
 11  
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 12  
  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 13  
  * specific language governing permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 import java.io.BufferedInputStream;
 17  
 import java.io.File;
 18  
 import java.io.FileInputStream;
 19  
 import java.io.FileNotFoundException;
 20  
 import java.util.Stack;
 21  
 import java.util.Vector;
 22  
 
 23  
 import javax.xml.parsers.SAXParser;
 24  
 import javax.xml.parsers.SAXParserFactory;
 25  
 
 26  
 import org.apache.commons.logging.Log;
 27  
 import org.apache.commons.logging.LogFactory;
 28  
 import org.apache.torque.engine.EngineException;
 29  
 import org.apache.torque.engine.database.model.Column;
 30  
 import org.apache.torque.engine.database.model.Database;
 31  
 import org.apache.torque.engine.database.model.Domain;
 32  
 import org.apache.torque.engine.database.model.ForeignKey;
 33  
 import org.apache.torque.engine.database.model.Index;
 34  
 import org.apache.torque.engine.database.model.Table;
 35  
 import org.apache.torque.engine.database.model.Unique;
 36  
 import org.kuali.core.db.torque.DatabaseParser;
 37  
 import org.xml.sax.Attributes;
 38  
 import org.xml.sax.InputSource;
 39  
 import org.xml.sax.SAXException;
 40  
 import org.xml.sax.SAXParseException;
 41  
 import org.xml.sax.helpers.DefaultHandler;
 42  
 
 43  
 /**
 44  
  * A Class that is used to parse an input xml schema file and creates a Database java structure.
 45  
  * 
 46  
  * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
 47  
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 48  
  * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
 49  
  * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
 50  
  * @author <a href="mailto:fischer@seitenbau.de">Thomas Fischer</a>
 51  
  * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
 52  
  * @version $Id: XmlToAppData.java,v 1.1 2007-10-21 07:57:26 abyrne Exp $
 53  
  */
 54  0
 public class XmlToAppData extends DefaultHandler implements DatabaseParser {
 55  
     /** Logging class from commons.logging */
 56  0
     private static Log log = LogFactory.getLog(XmlToAppData.class);
 57  
 
 58  
     private Database database;
 59  
     private Table currTable;
 60  
     private Column currColumn;
 61  
     private ForeignKey currFK;
 62  
     private Index currIndex;
 63  
     private Unique currUnique;
 64  
 
 65  
     private boolean firstPass;
 66  
     private boolean isExternalSchema;
 67  
     private String currentPackage;
 68  
     private String currentXmlFile;
 69  
     private String defaultPackage;
 70  
 
 71  
     private static SAXParserFactory saxFactory;
 72  
 
 73  
     /** remember all files we have already parsed to detect looping. */
 74  
     private Vector alreadyReadFiles;
 75  
 
 76  
     /** this is the stack to store parsing data */
 77  0
     private Stack parsingStack = new Stack();
 78  
 
 79  
     static {
 80  0
         saxFactory = SAXParserFactory.newInstance();
 81  0
         saxFactory.setValidating(true);
 82  0
     }
 83  
 
 84  
     /**
 85  
      * Creates a new instance for the specified database type.
 86  
      * 
 87  
      * @param databaseType
 88  
      *            The type of database for the application.
 89  
      */
 90  0
     public XmlToAppData(String databaseType) {
 91  0
         database = new Database(databaseType);
 92  0
         firstPass = true;
 93  0
     }
 94  
 
 95  
     /**
 96  
      * Creates a new instance for the specified database type.
 97  
      * 
 98  
      * @param databaseType
 99  
      *            The type of database for the application.
 100  
      * @param defaultPackage
 101  
      *            the default java package used for the om
 102  
      */
 103  0
     public XmlToAppData(String databaseType, String defaultPackage) {
 104  0
         database = new Database(databaseType);
 105  0
         this.defaultPackage = defaultPackage;
 106  0
         firstPass = true;
 107  0
     }
 108  
 
 109  
     /**
 110  
      * Parses a XML input file and returns a newly created and populated Database structure.
 111  
      * 
 112  
      * @param xmlFile
 113  
      *            The input file to parse.
 114  
      * @return Database populated by <code>xmlFile</code>.
 115  
      */
 116  
     public Database parseResource(String xmlFile) throws EngineException {
 117  
         try {
 118  
             // in case I am missing something, make it obvious
 119  0
             if (!firstPass) {
 120  0
                 throw new Error("No more double pass");
 121  
             }
 122  
             // check to see if we alread have parsed the file
 123  0
             if ((alreadyReadFiles != null) && alreadyReadFiles.contains(xmlFile)) {
 124  0
                 return database;
 125  0
             } else if (alreadyReadFiles == null) {
 126  0
                 alreadyReadFiles = new Vector(3, 1);
 127  
             }
 128  
 
 129  
             // remember the file to avoid looping
 130  0
             alreadyReadFiles.add(xmlFile);
 131  
 
 132  0
             currentXmlFile = xmlFile;
 133  
 
 134  0
             SAXParser parser = saxFactory.newSAXParser();
 135  
 
 136  0
             FileInputStream fileInputStream = null;
 137  
             try {
 138  0
                 fileInputStream = new FileInputStream(xmlFile);
 139  0
             } catch (FileNotFoundException fnfe) {
 140  0
                 throw new FileNotFoundException(new File(xmlFile).getAbsolutePath());
 141  0
             }
 142  0
             BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
 143  
             try {
 144  0
                 log.info("Parsing file: '" + (new File(xmlFile)).getName() + "'");
 145  0
                 InputSource is = new InputSource(bufferedInputStream);
 146  0
                 is.setSystemId(new File(xmlFile).getAbsolutePath());
 147  0
                 parser.parse(is, this);
 148  
             } finally {
 149  0
                 bufferedInputStream.close();
 150  0
             }
 151  0
         } catch (SAXParseException e) {
 152  0
             throw new EngineException("Sax error on line " + e.getLineNumber() + " column " + e.getColumnNumber()
 153  
                     + " : " + e.getMessage(), e);
 154  0
         } catch (Exception e) {
 155  0
             throw new EngineException(e);
 156  0
         }
 157  0
         if (!isExternalSchema) {
 158  0
             firstPass = false;
 159  
         }
 160  0
         database.doFinalInitialization();
 161  0
         return database;
 162  
     }
 163  
 
 164  
     /**
 165  
      * EntityResolver implementation. Called by the XML parser
 166  
      * 
 167  
      * @param publicId
 168  
      *            The public identifier of the external entity
 169  
      * @param systemId
 170  
      *            The system identifier of the external entity
 171  
      * @return an InputSource for the database.dtd file
 172  
      * @see DTDResolver#resolveEntity(String, String)
 173  
      */
 174  
     public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
 175  
         try {
 176  0
             return new DTDResolver().resolveEntity(publicId, systemId);
 177  0
         } catch (Exception e) {
 178  0
             throw new SAXException(e);
 179  
         }
 180  
     }
 181  
 
 182  
     /**
 183  
      * Handles opening elements of the xml file.
 184  
      * 
 185  
      * @param uri
 186  
      * @param localName
 187  
      *            The local name (without prefix), or the empty string if Namespace processing is not being performed.
 188  
      * @param rawName
 189  
      *            The qualified name (with prefix), or the empty string if qualified names are not available.
 190  
      * @param attributes
 191  
      *            The specified or defaulted attributes
 192  
      */
 193  
     public void startElement(String uri, String localName, String rawName, Attributes attributes) throws SAXException {
 194  
         try {
 195  0
             if (rawName.equals("database")) {
 196  0
                 if (isExternalSchema) {
 197  0
                     currentPackage = attributes.getValue("package");
 198  0
                     if (currentPackage == null) {
 199  0
                         currentPackage = defaultPackage;
 200  
                     }
 201  
                 } else {
 202  0
                     database.loadFromXML(attributes);
 203  0
                     if (database.getPackage() == null) {
 204  0
                         database.setPackage(defaultPackage);
 205  
                     }
 206  
                 }
 207  0
             } else if (rawName.equals("external-schema")) {
 208  0
                 String xmlFile = attributes.getValue("filename");
 209  0
                 if (xmlFile.charAt(0) != '/') {
 210  0
                     File f = new File(currentXmlFile);
 211  0
                     xmlFile = new File(f.getParent(), xmlFile).getPath();
 212  
                 }
 213  
 
 214  
                 // put current state onto the stack
 215  0
                 ParseStackElement.pushState(this);
 216  
 
 217  0
                 isExternalSchema = true;
 218  
 
 219  0
                 parseResource(xmlFile);
 220  
                 // get the last state from the stack
 221  0
                 ParseStackElement.popState(this);
 222  0
             } else if (rawName.equals("domain")) {
 223  0
                 Domain domain = new Domain();
 224  0
                 domain.loadFromXML(attributes, database.getPlatform());
 225  0
                 database.addDomain(domain);
 226  0
             } else if (rawName.equals("table")) {
 227  0
                 currTable = database.addTable(attributes);
 228  0
                 if (isExternalSchema) {
 229  0
                     currTable.setForReferenceOnly(true);
 230  0
                     currTable.setPackage(currentPackage);
 231  
                 }
 232  0
             } else if (rawName.equals("column")) {
 233  0
                 currColumn = currTable.addColumn(attributes);
 234  0
             } else if (rawName.equals("inheritance")) {
 235  0
                 currColumn.addInheritance(attributes);
 236  0
             } else if (rawName.equals("foreign-key")) {
 237  0
                 currFK = currTable.addForeignKey(attributes);
 238  0
             } else if (rawName.equals("reference")) {
 239  0
                 currFK.addReference(attributes);
 240  0
             } else if (rawName.equals("index")) {
 241  0
                 currIndex = currTable.addIndex(attributes);
 242  0
             } else if (rawName.equals("index-column")) {
 243  0
                 currIndex.addColumn(attributes);
 244  0
             } else if (rawName.equals("unique")) {
 245  0
                 currUnique = currTable.addUnique(attributes);
 246  0
             } else if (rawName.equals("unique-column")) {
 247  0
                 currUnique.addColumn(attributes);
 248  0
             } else if (rawName.equals("id-method-parameter")) {
 249  0
                 currTable.addIdMethodParameter(attributes);
 250  0
             } else if (rawName.equals("option")) {
 251  0
                 setOption(attributes);
 252  
             }
 253  0
         } catch (Exception e) {
 254  0
             throw new SAXException(e);
 255  0
         }
 256  0
     }
 257  
 
 258  
     /**
 259  
      * Handles closing elements of the xml file.
 260  
      * 
 261  
      * @param uri
 262  
      * @param localName
 263  
      *            The local name (without prefix), or the empty string if Namespace processing is not being performed.
 264  
      * @param rawName
 265  
      *            The qualified name (with prefix), or the empty string if qualified names are not available.
 266  
      */
 267  
     public void endElement(String uri, String localName, String rawName) throws SAXException {
 268  0
         if (log.isDebugEnabled()) {
 269  0
             log.debug("endElement(" + uri + ", " + localName + ", " + rawName + ") called");
 270  
         }
 271  
         try {
 272  
             // Reset working objects to null to allow option to know
 273  
             // which element it is associated with.
 274  0
             if (rawName.equals("table")) {
 275  0
                 currTable = null;
 276  0
             } else if (rawName.equals("column")) {
 277  0
                 currColumn = null;
 278  0
             } else if (rawName.equals("foreign-key")) {
 279  0
                 currFK = null;
 280  0
             } else if (rawName.equals("index")) {
 281  0
                 currIndex = null;
 282  0
             } else if (rawName.equals("unique")) {
 283  0
                 currUnique = null;
 284  
             }
 285  0
         } catch (Exception e) {
 286  0
             throw new SAXException(e);
 287  0
         }
 288  0
     }
 289  
 
 290  
     public void setOption(Attributes attributes) {
 291  
         // Look thru supported model elements in reverse order to
 292  
         // find one that this option statement applies to.
 293  
 
 294  0
         String key = attributes.getValue("key");
 295  0
         String value = attributes.getValue("value");
 296  0
         if (currUnique != null) {
 297  0
             currUnique.addOption(key, value);
 298  0
         } else if (currIndex != null) {
 299  0
             currIndex.addOption(key, value);
 300  0
         } else if (currFK != null) {
 301  0
             currFK.addOption(key, value);
 302  0
         } else if (currColumn != null) {
 303  0
             currColumn.addOption(key, value);
 304  0
         } else if (currTable != null) {
 305  0
             currTable.addOption(key, value);
 306  
         } else { // Must be a db level option.
 307  0
             database.addOption(key, value);
 308  
         }
 309  0
     }
 310  
 
 311  
     /**
 312  
      * Handles exception which occur when the xml file is parsed
 313  
      * 
 314  
      * @param e
 315  
      *            the exception which occured while parsing
 316  
      * @throws SAXException
 317  
      *             always
 318  
      */
 319  
     public void error(SAXParseException e) throws SAXException {
 320  0
         log.error("Sax parser threw an Exception", e);
 321  0
         throw new SAXException("Error while parsing " + currentXmlFile + " at line " + e.getLineNumber() + " column "
 322  
                 + e.getColumnNumber() + " : " + e.getMessage());
 323  
     }
 324  
 
 325  
     /**
 326  
      * When parsing multiple files that use nested <external-schema> tags we need to use a stack to remember some
 327  
      * values.
 328  
      */
 329  
     private static class ParseStackElement {
 330  
         private boolean isExternalSchema;
 331  
         private String currentPackage;
 332  
         private String currentXmlFile;
 333  
         private boolean firstPass;
 334  
 
 335  
         /**
 336  
          * 
 337  
          * @param parser
 338  
          */
 339  0
         public ParseStackElement(XmlToAppData parser) {
 340  
             // remember current state of parent object
 341  0
             isExternalSchema = parser.isExternalSchema;
 342  0
             currentPackage = parser.currentPackage;
 343  0
             currentXmlFile = parser.currentXmlFile;
 344  0
             firstPass = parser.firstPass;
 345  
 
 346  
             // push the state onto the stack
 347  0
             parser.parsingStack.push(this);
 348  0
         }
 349  
 
 350  
         /**
 351  
          * Removes the top element from the stack and activates the stored state
 352  
          * 
 353  
          * @param parser
 354  
          */
 355  
         public static void popState(XmlToAppData parser) {
 356  0
             if (!parser.parsingStack.isEmpty()) {
 357  0
                 ParseStackElement elem = (ParseStackElement) parser.parsingStack.pop();
 358  
 
 359  
                 // activate stored state
 360  0
                 parser.isExternalSchema = elem.isExternalSchema;
 361  0
                 parser.currentPackage = elem.currentPackage;
 362  0
                 parser.currentXmlFile = elem.currentXmlFile;
 363  0
                 parser.firstPass = elem.firstPass;
 364  
             }
 365  0
         }
 366  
 
 367  
         /**
 368  
          * Stores the current state on the top of the stack.
 369  
          * 
 370  
          * @param parser
 371  
          */
 372  
         public static void pushState(XmlToAppData parser) {
 373  0
             new ParseStackElement(parser);
 374  0
         }
 375  
     }
 376  
 }