1 package org.apache.torque.engine.database.transform;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Turbine" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache Turbine", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import java.io.BufferedReader;
58 import java.io.File;
59 import java.io.FileNotFoundException;
60 import java.io.FileReader;
61 import java.util.Stack;
62 import java.util.Vector;
63
64 import javax.xml.parsers.SAXParser;
65 import javax.xml.parsers.SAXParserFactory;
66
67 import org.apache.commons.logging.Log;
68 import org.apache.commons.logging.LogFactory;
69 import org.apache.torque.engine.EngineException;
70 import org.apache.torque.engine.database.model.AppData;
71 import org.apache.torque.engine.database.model.Column;
72 import org.apache.torque.engine.database.model.Database;
73 import org.apache.torque.engine.database.model.ForeignKey;
74 import org.apache.torque.engine.database.model.Index;
75 import org.apache.torque.engine.database.model.Table;
76 import org.apache.torque.engine.database.model.Unique;
77 import org.xml.sax.Attributes;
78 import org.xml.sax.InputSource;
79 import org.xml.sax.SAXException;
80 import org.xml.sax.helpers.DefaultHandler;
81
82 /***
83 * A Class that is used to parse an input xml schema file and creates an AppData
84 * java structure.
85 *
86 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
87 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
88 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
89 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
90 * @version $Id: XmlToAppData.java,v 1.8 2003/08/25 21:31:22 mpoeschl Exp $
91 */
92 public class XmlToAppData extends DefaultHandler
93 {
94 /*** Logging class from commons.logging */
95 private static Log log = LogFactory.getLog(XmlToAppData.class);
96
97 private AppData app;
98 private Database currDB;
99 private Table currTable;
100 private Column currColumn;
101 private ForeignKey currFK;
102 private Index currIndex;
103 private Unique currUnique;
104
105 private boolean firstPass;
106 private boolean isExternalSchema;
107 private String currentPackage;
108 private String currentXmlFile;
109 private String defaultPackage;
110
111 private static SAXParserFactory saxFactory;
112
113 /*** remember all files we have already parsed to detect looping. */
114 private Vector alreadyReadFiles;
115
116 /*** this is the stack to store parsing data */
117 private Stack parsingStack = new Stack();
118
119 static
120 {
121 saxFactory = SAXParserFactory.newInstance();
122 saxFactory.setValidating(true);
123 }
124
125 /***
126 * Creates a new instance for the specified database type.
127 *
128 * @param databaseType The type of database for the application.
129 * @param defaultPackage the default java package used for the om
130 * @param basePropsFilePath The base of the path to the properties
131 * file, including trailing slash.
132 */
133 public XmlToAppData(String databaseType, String defaultPackage,
134 String basePropsFilePath)
135 {
136 app = new AppData(databaseType, basePropsFilePath);
137 this.defaultPackage = defaultPackage;
138 firstPass = true;
139 }
140
141 /***
142 * Parses a XML input file and returns a newly created and
143 * populated AppData structure.
144 *
145 * @param xmlFile The input file to parse.
146 * @return AppData populated by <code>xmlFile</code>.
147 */
148 public AppData parseFile(String xmlFile)
149 throws EngineException
150 {
151 try
152 {
153 // in case I am missing something, make it obvious
154 if (!firstPass)
155 {
156 throw new Error("No more double pass");
157 }
158 // check to see if we alread have parsed the file
159 if ((alreadyReadFiles != null)
160 && alreadyReadFiles.contains(xmlFile))
161 {
162 return app;
163 }
164 else if (alreadyReadFiles == null)
165 {
166 alreadyReadFiles = new Vector(3, 1);
167 }
168
169 // remember the file to avoid looping
170 alreadyReadFiles.add(xmlFile);
171
172 currentXmlFile = xmlFile;
173
174 SAXParser parser = saxFactory.newSAXParser();
175
176 FileReader fr = null;
177 try
178 {
179 fr = new FileReader(xmlFile);
180 }
181 catch (FileNotFoundException fnfe)
182 {
183 throw new FileNotFoundException
184 (new File(xmlFile).getAbsolutePath());
185 }
186 BufferedReader br = new BufferedReader(fr);
187 try
188 {
189 log.info("Parsing file: '"
190 + (new File(xmlFile)).getName() + "'");
191 InputSource is = new InputSource(br);
192 parser.parse(is, this);
193 }
194 finally
195 {
196 br.close();
197 }
198 }
199 catch (Exception e)
200 {
201 throw new EngineException(e);
202 }
203 if (!isExternalSchema)
204 {
205 firstPass = false;
206 }
207 return app;
208 }
209
210 /***
211 * EntityResolver implementation. Called by the XML parser
212 *
213 * @param publicId The public identifier of the external entity
214 * @param systemId The system identifier of the external entity
215 * @return an InputSource for the database.dtd file
216 * @see org.apache.torque.engine.database.transform.DTDResolver#resolveEntity(String, String)
217 */
218 public InputSource resolveEntity(String publicId, String systemId)
219 throws SAXException
220 {
221 try
222 {
223 return new DTDResolver().resolveEntity(publicId, systemId);
224 }
225 catch (Exception e)
226 {
227 throw new SAXException(e);
228 }
229 }
230
231
232 /***
233 * Handles opening elements of the xml file.
234 *
235 * @param uri
236 * @param localName The local name (without prefix), or the empty string if
237 * Namespace processing is not being performed.
238 * @param rawName The qualified name (with prefix), or the empty string if
239 * qualified names are not available.
240 * @param attributes The specified or defaulted attributes
241 */
242 public void startElement(String uri, String localName, String rawName,
243 Attributes attributes)
244 throws SAXException
245 {
246 try
247 {
248 if (rawName.equals("database"))
249 {
250 if (isExternalSchema)
251 {
252 currentPackage = attributes.getValue("package");
253 if (currentPackage == null)
254 {
255 currentPackage = defaultPackage;
256 }
257 }
258 else
259 {
260 currDB = app.addDatabase(attributes);
261 if (currDB.getPackage() == null)
262 {
263 currDB.setPackage(defaultPackage);
264 }
265 }
266 }
267 else if (rawName.equals("external-schema"))
268 {
269 String xmlFile = attributes.getValue("filename");
270 if (xmlFile.charAt(0) != '/')
271 {
272 File f = new File(currentXmlFile);
273 xmlFile = new File(f.getParent(), xmlFile).getPath();
274 }
275
276 // put current state onto the stack
277 ParseStackElement.pushState(this);
278
279 isExternalSchema = true;
280
281 parseFile(xmlFile);
282 // get the last state from the stack
283 ParseStackElement.popState(this);
284 }
285 else if (rawName.equals("table"))
286 {
287 currTable = currDB.addTable(attributes);
288 if (isExternalSchema)
289 {
290 currTable.setForReferenceOnly(true);
291 currTable.setPackage(currentPackage);
292 }
293 }
294 else if (rawName.equals("column"))
295 {
296 currColumn = currTable.addColumn(attributes);
297 }
298 else if (rawName.equals("inheritance"))
299 {
300 currColumn.addInheritance(attributes);
301 }
302 else if (rawName.equals("foreign-key"))
303 {
304 currFK = currTable.addForeignKey(attributes);
305 }
306 else if (rawName.equals("reference"))
307 {
308 currFK.addReference(attributes);
309 }
310 else if (rawName.equals("index"))
311 {
312 currIndex = currTable.addIndex(attributes);
313 }
314 else if (rawName.equals("index-column"))
315 {
316 currIndex.addColumn(attributes);
317 }
318 else if (rawName.equals("unique"))
319 {
320 currUnique = currTable.addUnique(attributes);
321 }
322 else if (rawName.equals("unique-column"))
323 {
324 currUnique.addColumn(attributes);
325 }
326 else if (rawName.equals("id-method-parameter"))
327 {
328 currTable.addIdMethodParameter(attributes);
329 }
330 }
331 catch (Exception e)
332 {
333 throw new SAXException(e);
334 }
335 }
336
337 /***
338 * Handles closing elements of the xml file.
339 *
340 * @param uri
341 * @param localName The local name (without prefix), or the empty string if
342 * Namespace processing is not being performed.
343 * @param rawName The qualified name (with prefix), or the empty string if
344 * qualified names are not available.
345 */
346 public void endElement(String uri, String localName, String rawName)
347 {
348 if (log.isDebugEnabled())
349 {
350 log.debug("endElement(" + uri + ", " + localName + ", "
351 + rawName + ") called");
352 }
353 }
354
355 /***
356 * When parsing multiple files that use nested <external-schema> tags we
357 * need to use a stack to remember some values.
358 */
359 private static class ParseStackElement
360 {
361 private boolean isExternalSchema;
362 private String currentPackage;
363 private String currentXmlFile;
364 private boolean firstPass;
365
366 /***
367 *
368 * @param parser
369 */
370 public ParseStackElement(XmlToAppData parser)
371 {
372 // remember current state of parent object
373 isExternalSchema = parser.isExternalSchema;
374 currentPackage = parser.currentPackage;
375 currentXmlFile = parser.currentXmlFile;
376 firstPass = parser.firstPass;
377
378 // push the state onto the stack
379 parser.parsingStack.push(this);
380 }
381
382 /***
383 * Removes the top element from the stack and activates the stored state
384 *
385 * @param parser
386 */
387 public static void popState(XmlToAppData parser)
388 {
389 if (!parser.parsingStack.isEmpty())
390 {
391 ParseStackElement elem = (ParseStackElement)
392 parser.parsingStack.pop();
393
394 // activate stored state
395 parser.isExternalSchema = elem.isExternalSchema;
396 parser.currentPackage = elem.currentPackage;
397 parser.currentXmlFile = elem.currentXmlFile;
398 parser.firstPass = elem.firstPass;
399 }
400 }
401
402 /***
403 * Stores the current state on the top of the stack.
404 *
405 * @param parser
406 */
407 public static void pushState(XmlToAppData parser)
408 {
409 new ParseStackElement(parser);
410 }
411 }
412 }
This page was automatically generated by Maven