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