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