001: /*
002: * ====================================================================
003: * JAFFA - Java Application Framework For All
004: *
005: * Copyright (C) 2002 JAFFA Development Group
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * Redistribution and use of this software and associated documentation ("Software"),
022: * with or without modification, are permitted provided that the following conditions are met:
023: * 1. Redistributions of source code must retain copyright statements and notices.
024: * Redistributions must also contain a copy of this document.
025: * 2. Redistributions in binary form must reproduce the above copyright notice,
026: * this list of conditions and the following disclaimer in the documentation
027: * and/or other materials provided with the distribution.
028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
029: * this Software without prior written permission. For written permission,
030: * please contact mail to: jaffagroup@yahoo.com.
031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
032: * appear in their names without prior written permission.
033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
034: *
035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: */
049: package org.jaffa.tools.domainmeta.erwin;
050:
051: import java.io.File;
052: import java.io.FileOutputStream;
053: import java.io.FileWriter;
054: import java.io.IOException;
055: import java.io.PrintWriter;
056: import java.io.StringWriter;
057: import java.io.Writer;
058: import java.lang.RuntimeException;
059: import java.util.ArrayList;
060: import java.util.HashMap;
061: import java.util.Iterator;
062: import java.util.LinkedHashMap;
063: import java.util.List;
064: import java.util.Properties;
065: import java.util.regex.*;
066: import org.apache.log4j.Logger;
067: import org.jaffa.datatypes.DateTime;
068: import org.jaffa.datatypes.Defaults;
069: import org.jaffa.tools.domainmeta.common.FieldBean;
070: import org.jaffa.tools.domainmeta.common.TableBean;
071: import org.jaffa.util.ListProperties;
072: import org.jaffa.util.SplitString;
073: import org.jaffa.util.StringHelper;
074: import org.jaffa.util.URLHelper;
075: import org.jaffa.util.XmlHelper;
076: import org.jdom.Document;
077: import org.jdom.Element;
078: import org.jdom.input.SAXBuilder;
079:
080: /**
081: * ErWinSchemaReader - This class will read a ErWin Schema, and generate a set of domain
082: * object descriptors which represent the model. This first version is very basic, and makes
083: * some BIG assumptions about how to implement relationships between tables.
084: * <p>
085: * <b>Version 1.1</b>
086: * <ul>
087: * <li>Removed use of static variables
088: * <li>This also generates an ApplicationsResources.fragment for the labels of all the fields
089: * as referenced in the meta data, this file should be placed in the module root directory
090: * in the java source once the patterns have been generated.
091: * <li>Supports file format v4100
092: * <p>
093: * <b>Version 1.2</b>
094: * <li>Now generated the v1.1 domain objects, and new fragment filename
095: * <li>Uses a common version of Table/Field bean, that supports name translation,
096: * included reserved keywords
097: * <li>Supports latest release of ErWin, format v4252
098: * <li>Ignores tables with no primary key defintion
099: * <li>Doesn't include label properties for ignored tables, and now puts the
100: * table name label in the Domain Object XML
101: *<p>
102: *
103: * @author PaulE
104: * @version 1.2
105: */
106: public class ErWinSchemaReader {
107:
108: private static Logger log = Logger
109: .getLogger(ErWinSchemaReader.class);
110: private static String ENTITY_FILTER = null;
111:
112: private String m_inFile;
113: private String m_outDir;
114: private String m_packagePrefix;
115: private String m_appName;
116: private String m_moduleName;
117: private boolean m_fullPackage = true;
118:
119: /** Creates a new instance of ErWinSchemaReader
120: * @param file Name of the ErWin XML file to process
121: * @throws Exception throw if any processing error occurs
122: */
123: public ErWinSchemaReader(String file, String outDir,
124: String packagePrefix, String app, String module,
125: boolean full) throws Exception {
126:
127: // Keep Copy Of Input Params
128: m_inFile = file;
129: m_outDir = outDir;
130: m_packagePrefix = packagePrefix;
131: m_appName = app;
132: m_moduleName = module;
133: m_fullPackage = full;
134:
135: // Initialize other data
136: String packageName = m_packagePrefix.toLowerCase()
137: + (m_fullPackage ? ".applications." : ".")
138: + m_appName.toLowerCase()
139: + (m_fullPackage ? ".modules." : ".")
140: + m_moduleName.toLowerCase() + ".domain";
141: String labelPrefix = "label."
142: + StringHelper.getUpper1(m_appName) + "."
143: + StringHelper.getUpper1(m_moduleName) + ".";
144: ListProperties moduleLabels = new ListProperties();
145: HashMap tableList = new HashMap();
146: ArrayList duplicateFields = new ArrayList();
147:
148: // Load in the ErWin XML File
149: SAXBuilder builder = new SAXBuilder();
150: builder.setValidation(false);
151: builder.setIgnoringElementContentWhitespace(true);
152: Document doc = builder
153: .build(URLHelper.newExtendedURL(m_inFile));
154:
155: // Make sure it is the same version of the XML that this program was coded for
156: if (!"4100".equals(doc.getRootElement().getAttributeValue(
157: "FileVersion"))
158: && !"4252".equals(doc.getRootElement()
159: .getAttributeValue("FileVersion")))
160: //throw new RuntimeException("Wrong Version Of ErWin XML File...Use at your own risk!");
161: System.out
162: .println("This tool has built tested on file versions 4100,4252 of ErWin\n"
163: + "This file is Version "
164: + doc.getRootElement().getAttributeValue(
165: "FileVersion")
166: + " there may be incompatabilities");
167:
168: // Loop through all the entities and build TableBean objects
169: List entities = doc.getRootElement().getChild("Model")
170: .getChild("Entity_Groups").getChildren("Entity");
171: for (Iterator i = entities.iterator(); i.hasNext();) {
172: boolean error = false;
173: Element entity = (Element) i.next();
174: String entityName = entity.getAttributeValue("Name");
175: String entityNameJava = makeName(entityName);
176: String tableName = entity.getChild("EntityProps")
177: .getChildText("Physical_Name");
178: if (tableName == null)
179: tableName = entityName;
180:
181: if (ENTITY_FILTER != null
182: && (!Pattern.matches(ENTITY_FILTER, tableName)))
183: continue;
184:
185: // Create the tableBean
186: TableBean tb = new TableBean();
187: tb.setId(entity.getAttributeValue("id"));
188: tb.setDomainName(entityNameJava);
189: tb.setDomainPackage(packageName);
190: tb.setTableName(tableName);
191:
192: // Loop through all the attributes and create a FieldBean in the
193: // TableBean for each one
194: LinkedHashMap properties = new LinkedHashMap();
195: Iterator i1 = null;
196: try {
197: i1 = entity.getChild("Attribute_Groups").getChildren(
198: "Attribute").iterator();
199: } catch (NullPointerException e) {
200: log.error("Entity " + entityNameJava
201: + " has no attributes, skipping...");
202: error = true;
203: continue;
204: }
205: for (; i1.hasNext();) {
206: Element field = (Element) i1.next();
207: FieldBean fb = new FieldBean();
208: String id = parseField(fb, field);
209: if (hasAttrib(properties, fb.getFieldName())) {
210: duplicateFields.add(id);
211: log.warn("Duplicate attribute " + id
212: + " found in entity " + tableName
213: + ". It will be ignored");
214: } else {
215: properties.put(id, fb);
216:
217: log.debug("Add Field - " + fb.getFieldName()
218: + " id=" + id);
219:
220: // Set the label text if null, and add this label to the properties
221: if (fb.getLabelText() == null)
222: fb.setLabelText(StringHelper
223: .getSpace(StringHelper.getUpper1(fb
224: .getPropertyName())));
225:
226: String label = labelPrefix
227: + StringHelper
228: .getUpper1(tb.getDomainName())
229: + "."
230: + StringHelper.getUpper1(fb
231: .getPropertyName());
232: fb.setLabel("[" + label + "]");
233: //moduleLabels.put(label, fb.getLabelText()); --- Defered untils the table is added!
234: }
235: }
236: // Add the fields to the table
237: tb.setFields(properties);
238:
239: // Find out what fields are in the Primary Key....
240: // For any field found, update the FieldBean to show this
241: Iterator i2 = null;
242: try {
243: i2 = entity.getChild("Key_Group_Groups").getChildren(
244: "Key_Group").iterator();
245: } catch (NullPointerException e) {
246: log.error("Entity " + entityNameJava + " has no keys!");
247: error = true;
248: continue;
249: }
250: if (i2 != null)
251: for (; i2.hasNext();) {
252: Element key = (Element) i2.next();
253: //System.out.println("Key Id= " + key.getAttributeValue("id"));
254: if ("PK".equals(key.getChild("Key_GroupProps")
255: .getChildText("Key_Group_Type"))) {
256:
257: Iterator i3 = null;
258: try {
259: i3 = key
260: .getChild("Key_Group_Member_Groups")
261: .getChildren("Key_Group_Member")
262: .iterator();
263: } catch (NullPointerException e) {
264: log.error("Entity " + entityNameJava
265: + " has no fields in PK!");
266: error = true;
267: break;
268: }
269: if (i3 != null)
270: for (; i3.hasNext();) {
271: Element keyfield = (Element) i3.next();
272: //System.out.println("Key Member Id= " + keyfield.getAttributeValue("id") + "/" + keyfield.getAttributeValue("Name"));
273: String fieldId = keyfield
274: .getChild(
275: "Key_Group_MemberProps")
276: .getChildText(
277: "Key_Group_Member_Column");
278: // look up this key
279: if (!properties.containsKey(fieldId)) {
280: if (duplicateFields
281: .contains(fieldId))
282: log
283: .warn("Primary Key Referenced an Ignored Duplicate Field. Will be ignored from the primary key too!");
284: else
285: throw new RuntimeException(
286: "Primary Key Ref Not Found..."
287: + tableName);
288: } else
289: // Set this field as a Key now.
290: ((FieldBean) properties
291: .get(fieldId))
292: .setPrimaryKey(true);
293: }
294: }
295: }
296:
297: if (!error) {
298: System.out.println("INFO: Creating Entity "
299: + entityNameJava + ", Table=" + tableName);
300: // Add table to the list
301: tableList.put(tb.getId(), tb);
302: // Write out a property for the table label
303: String label = labelPrefix
304: + StringHelper.getUpper1(tb.getDomainName());
305: String labelText = StringHelper.getSpace(tb
306: .getDomainName());
307: tb.setLabel("[" + label + "]");
308: moduleLabels.put(label, labelText);
309: // Write out all the field labels
310: if (tb.getFields() != null && !tb.getFields().isEmpty())
311: for (Iterator fi = tb.getFields().keySet()
312: .iterator(); fi.hasNext();) {
313: String key = (String) fi.next();
314: FieldBean fb = (FieldBean) tb.getFields().get(
315: key);
316: if (fb != null && fb.getLabel() != null)
317: moduleLabels.put(fb.getLabel().substring(1,
318: fb.getLabel().length() - 1), fb
319: .getLabelText());
320: }
321:
322: }
323: }
324:
325: // Now do a second pass once all TableBeans have been creates,
326: // to figure out what relationships are defined between them
327: List rels = doc.getRootElement().getChild("Model").getChild(
328: "Relationship_Groups").getChildren("Relationship");
329: for (Iterator i = rels.iterator(); i.hasNext();) {
330: Element rel = (Element) i.next();
331: String fromEnt = rel.getChild("RelationshipProps")
332: .getChildText("Relationship_Parent_Entity");
333: if (!tableList.containsKey(fromEnt)) {
334: log.error("Relationship Parent " + fromEnt
335: + " Not Found. Id=" + rel.getAttribute("id"));
336: continue;
337: }
338: String toEnt = rel.getChild("RelationshipProps")
339: .getChildText("Relationship_Child_Entity");
340: if (!tableList.containsKey(toEnt)) {
341: log.error("Relationship Child " + toEnt
342: + " Not Found. Id=" + rel.getAttribute("id"));
343: continue;
344: }
345: TableBean from = (TableBean) tableList.get(fromEnt);
346: TableBean to = (TableBean) tableList.get(toEnt);
347: from.addRelationship(to, true);
348: }
349:
350: // Now lets generate the files
351: for (Iterator i = tableList.keySet().iterator(); i.hasNext();) {
352: String key = (String) i.next();
353: TableBean tb = (TableBean) tableList.get(key);
354:
355: String fileName = m_outDir + File.separator
356: + tb.getDomainName() + ".xml";
357: PrintWriter out = new PrintWriter(new FileWriter(fileName));
358:
359: writeHeader(out);
360: tb
361: .write(out, "resources/jdbcengine",
362: "patterns/library/domain_creator_1_1/DomainCreatorPattern.xml");
363: writeFooter(out);
364: out.close();
365: }
366:
367: // Write out labels
368: String labelFileName = m_outDir + File.separator
369: + "ApplicationResources.pfragment";
370: String header = "Generated from file " + m_inFile + " on "
371: + (new DateTime());
372: FileOutputStream fos = new FileOutputStream(labelFileName);
373: try {
374: moduleLabels.sort();
375: moduleLabels.store(fos, header);
376: } catch (IOException e) {
377: System.out.println("Failed to write out labels to - "
378: + labelFileName);
379: System.out.println("Reason - " + e.getMessage());
380: }
381:
382: }
383:
384: private static boolean hasAttrib(HashMap attrs, String attrib) {
385: for (Iterator i = attrs.keySet().iterator(); i.hasNext();) {
386: String id = (String) i.next();
387: FieldBean fb = (FieldBean) attrs.get(id);
388: if (fb.getFieldName().equals(attrib))
389: return true;
390: }
391: return false;
392: }
393:
394: /** Return the id, and populate the bean */
395: private static String parseField(FieldBean fb, Element field) {
396: String id = field.getAttributeValue("id");
397:
398: // Get the names
399: String propName = field.getAttributeValue("Name");
400: String propNameJava = makeName(propName);
401: String fieldName = field.getChild("AttributeProps")
402: .getChildText("Physical_Name");
403: if (fieldName == null)
404: fieldName = propName;
405: // replace any spaces in the field name with '_'
406: fieldName = fieldName.replace(' ', '_');
407: fb.setPropertyName(propNameJava);
408: fb.setFieldName(fieldName);
409:
410: // Get Data Type
411: String dataType = field.getChild("AttributeProps")
412: .getChildText("Datatype");
413: if (dataType == null)
414: dataType = "varchar(50)";
415: String dataSize = null;
416: Pattern p2 = Pattern.compile("(.*?)\\((.*?)\\)");
417: Matcher m2 = p2.matcher(dataType);
418: if (m2.matches()) {
419: dataType = m2.group(1);
420: dataSize = m2.group(2);
421: }
422: // Get the SQL and Java data types
423: String javaType = null;
424: if ("VARCHAR".equalsIgnoreCase(dataType)
425: || "VARCHAR2".equalsIgnoreCase(dataType)
426: || "CHAR".equalsIgnoreCase(dataType))
427: javaType = Defaults.STRING;
428: else if ("NUMERIC".equalsIgnoreCase(dataType)
429: || "NUMBER".equalsIgnoreCase(dataType)) {
430: if (dataSize != null
431: && (dataSize.indexOf('.') == -1 || dataSize
432: .indexOf(',') == -1))
433: javaType = Defaults.DECIMAL;
434: else
435: javaType = Defaults.INTEGER;
436: } else if ("INT".equalsIgnoreCase(dataType)
437: || "INTEGER".equalsIgnoreCase(dataType)
438: || "LONG".equalsIgnoreCase(dataType))
439: javaType = Defaults.INTEGER;
440: else if ("SMALLINT".equalsIgnoreCase(dataType))
441: javaType = Defaults.INTEGER;
442: else if ("DATE".equalsIgnoreCase(dataType))
443: javaType = Defaults.DATETIME;
444: else
445: throw new RuntimeException("Need to map data type - "
446: + dataType);
447:
448: String javaTypeClass = Defaults.getClassString(javaType);
449: if (javaTypeClass != null && javaTypeClass.equals("[B"))
450: javaTypeClass = "byte[]";
451:
452: fb.setJavaDataType(javaTypeClass);
453: fb.setSqlDataType(javaType);
454: if (dataSize != null) {
455: if (dataSize.indexOf(".") != -1) {
456: SplitString ss = new SplitString(dataSize, ".");
457: fb.setIntSize(ss.getPrefix());
458: fb.setFracSize(ss.getSuffix());
459: } else if (dataSize.indexOf(",") != -1) {
460: SplitString ss = new SplitString(dataSize, ",");
461: fb.setIntSize(ss.getPrefix());
462: fb.setFracSize(ss.getSuffix());
463: } else {
464: fb.setIntSize(dataSize);
465: }
466: }
467: // Make sure lengths are not null
468: if ("DECIMAL".equalsIgnoreCase(javaType)
469: && fb.getFracSize() == null)
470: fb.setFracSize("0");
471: if (("DECIMAL".equalsIgnoreCase(javaType) || "INTEGER"
472: .equalsIgnoreCase(javaType))
473: && fb.getIntSize() == null)
474: fb.setIntSize("8");
475:
476: //System.out.println(" Property:" + fb);
477: // return the key of this object
478: return id;
479: }
480:
481: private static void writeHeader(PrintWriter out) {
482: out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
483: out
484: .println("<!DOCTYPE Root PUBLIC \"-//JAFFA//DTD Domain Creator Meta 1.1//EN\" \"http://jaffa.sourceforge.net/DTD/domainCreatorMeta_1_1.dtd\">");
485: out.println("<Root>");
486: }
487:
488: private static void writeFooter(PrintWriter out) {
489: out.println("</Root>");
490: }
491:
492: private static String makeName(String name) {
493: name = StringHelper.replace(name, "_", " ");
494: String[] l = StringHelper.parseString(name, " ");
495: StringBuffer n = new StringBuffer();
496: if (l != null && l.length != 0)
497: for (int i = 0; i < l.length; i++)
498: n.append(StringHelper.getUpper1(l[i].toLowerCase()));
499: return n.toString();
500: }
501:
502: /** Entry point for running the tool. The following parameters
503: * should be supplied....
504: * [0] = Input Erwin XML File
505: * [1] = Package For Domain Objects
506: * [2] = Output directory for XML Files
507: *
508: * @param args the command line arguments
509: */
510: public static void main(String[] args) {
511: int exit = 0;
512: boolean full = true;
513:
514: System.out.println("Start: ErWin Schema Reader...");
515:
516: if (args == null || args.length < 5 || args.length > 6) {
517: help();
518: exit = -1;
519: } else {
520: // Get package type
521: if (args.length == 6) {
522: if (args[5].toLowerCase().equals("full"))
523: full = true;
524: else if (args[5].toLowerCase().equals("short"))
525: full = false;
526: else {
527: help();
528: exit = -1;
529: }
530: }
531: }
532:
533: if (exit == 0) {
534: // Debug Messgaes
535: System.out.println(" Source Schema = " + args[0]);
536: System.out.println(" Output Path = " + args[1]);
537: System.out.println(" Package Prefix = " + args[2]);
538: System.out.println(" Package AppName= " + args[3]);
539: System.out.println(" Package Module = " + args[4]);
540: System.out.println(" Full Package = " + full);
541: try {
542: new ErWinSchemaReader(args[0], args[1], args[2],
543: args[3], args[4], full);
544: } catch (Exception e) {
545: e.printStackTrace();
546: exit = -1;
547: }
548: }
549: System.out.println("End: ErWin Schema Reader...");
550: System.exit(exit);
551: }
552:
553: private static void help() {
554: System.out
555: .println("Generate Domain Object Descriptors from an ErWin XML file...");
556: System.out.println("");
557: System.out
558: .println("ErWinSchemaReader source outdir prefix app module [FULL|SHORT]");
559: System.out.println("");
560: System.out.println(" source = Location of source ErWin File");
561: System.out
562: .println(" outdir = Ouput directory for all generated files");
563: System.out
564: .println(" prefix = Package prefix to use for generated objects");
565: System.out.println(" app = Name of application package");
566: System.out.println(" module = Name of module package");
567: System.out
568: .println(" FULL = Use full package names (.applications & .modules) [DEFAULT]");
569: System.out.println(" SHORT = Use short package names");
570: System.out.println("");
571: System.out.println("Example");
572: System.out
573: .println(" ErWinSchemaReader c:\\erwin.xml c:\\temp org.jaffa sample user FULL");
574: System.out.println("");
575: }
576: }
|