/** * * C O P Y R I G H T N O T I C E * Copyright (c) 2001 by: * * The MicroArray Gene Expression Database group (MGED) * * Rosetta Inpharmatics * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * @author $Author: rhubley $ * @version $Revision: 1.6 $ * */ package org.biomage.tools.generate_dtd; import java.io.File; import java.io.FileWriter; import java.io.FileReader; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Vector; import org.biomage.tools.generate_classes.CreateFile; import org.biomage.tools.generate_classes.XMIParseHelpers; import org.biomage.tools.helpers.StringOutputHelpers; import org.w3c.dom.*; /** * Description: * Class that is resposible for generating a DTD file * for the classes represented by the list of class nodes * passed into the constructor. * */ public class WriteDTDFile { /** * Description: * The top level class. Even though it has no roles, must not * be treated the same since it has no roles because it is the * top element */ static protected Vector topElementNames = new Vector(); protected void setTopElements( Element topElements ) throws Exception { NodeList elements = topElements.getElementsByTagName("element"); for (int i = 0; i < elements.getLength(); i++) { topElementNames.add(XMIParseHelpers.getElementText((Element) elements.item(i))); } } /** * Description: * Utility method to verify whether class has roles or is top-level. * *
* @param createFile: the class to check. *
* */ protected static boolean isReferencedClass( CreateFile createFile ) { return (null != createFile.getRoleNames() && 0 < createFile.getRoleNames().size()) || topElementNames.contains(createFile.getClassFileName()); } /** * Description: * An interface implemented by classes that take a class list and transform the * members by either changing the relationships or adding or subtracting classes. */ static public interface CreateClassTransformer { public void transform( Vector createFileList ) throws Exception; } /** * Description: * A class that implements the transformation interface. Can be null. */ // BUGBUG: must come from resource protected CreateClassTransformer transformer = null; /** * Description: * Constructor for the DTD file generator. * *
* @param classFiles: information on the DTD Elements to write. * @param dtdFileName: what to name the output file. * @param outputDir: where to write the file. * @param header: templete file for the dtd header information. * @param transformClassName: optional name of the class to transform the classFiles. * @param packageOrder: order of the packages for the MAGE-ML elemnt and order of the lists * for the packages. * @param topElements: classes not referenced by any other class in the model itself. *
*/ public WriteDTDFile( Vector classFiles, String dtdFileName, String outputDir, File header, String transformClassName, Element topElements ) throws Exception { // If a transform class is specified, transform the class list if (null != transformClassName && 0 != transformClassName.length()) { Class transformClass = Class.forName(transformClassName); transformer = (CreateClassTransformer) transformClass.newInstance(); StringOutputHelpers.writeOutput("Calling " + transformClassName + " to transform the class list.", 0); transformer.transform(classFiles); } setTopElements(topElements); // Order the class files by inheritence/package classFiles = sortWriteFiles(classFiles); // Get the vector of write files Vector writeFiles = CreateWriteFiles(classFiles); // Have the write files generate their initial xml for (int i = 0; i < writeFiles.size(); i++) { ((WriteDTDElement) writeFiles.get(i)).createXMLStrings(); } // Create the writer String path = ""; if (null != outputDir) { path = outputDir + File.separatorChar; } FileWriter writer = new FileWriter( path + dtdFileName); writeHeader(writer, header); // Have the write files write out their entities StringOutputHelpers.writeOutput("Writing Entities.", 0); for (int i = 0; i < writeFiles.size(); i++) { ((WriteDTDElement) writeFiles.get(i)).writeEntities(writer); } // Have the write files write the elements and attlists StringOutputHelpers.writeOutput("Writing Elements and Attributes.", 0); for (int i = 0; i < writeFiles.size(); i++) { ((WriteDTDElement) writeFiles.get(i)).writeBody(writer); } writer.close(); } /** * Description: * Create the write classes for each of the class files. * *
* @param classFiles: information on the DTD Elements to write. *
*/ public Vector CreateWriteFiles( Vector classFiles ) throws Exception { Vector writeFiles = new Vector(200); for( int i = 0; i < classFiles.size(); i++) { CreateFile createFile = (CreateFile) classFiles.elementAt(i); if ( CreateFile.UML_CLASS == createFile.getFileType() ) { writeFiles.add(new WriteDTDClassElement(createFile)); } else if ( CreateFile.UML_PACKAGE == createFile.getFileType() ) { writeFiles.add(new WriteDTDPackageElement(createFile)); } else if ( CreateFile.UML_MODEL == createFile.getFileType() ) { writeFiles.add(new WriteDTDMageElement(createFile)); } } return writeFiles; } /** * Description: * An inner class to hold the inter and intra package * dependencies. */ static protected class PackageDependencies { /** * Description: * Names of packages this package is dependent on.. */ Set dependentOn = new HashSet(); /** * Description: * The classes that belong to this package. */ Vector createFiles = new Vector(); } /** * Description: * Create the write classes for each of the class files. * *
* @param classFiles: information on the DTD Elements to write. *
*/ // Now that I finally figured out how to do this, I think this can be done // much easier. CreateFile now has a method to return its base class CreateFile // which might help satisfy an absolute ordering needed to implement comparable. // // There is likely better ways to manipulate the various Collections, I have // a good working knowledge of them but haven't used them together like // this in other projects. // --mm static public Vector sortWriteFiles( Vector classFiles ) throws Exception { StringOutputHelpers.writeOutput("Sorting CreateFiles by Inheritence Hierarchy and Package", 0); Map name2dependencies = new HashMap(); for(int i = 0; i < classFiles.size(); i++) { // Put each class into its dependency class CreateFile createFile = (CreateFile) classFiles.elementAt(i); String className = createFile.getClassFileName(); String packageName = createFile.getPackageName(); PackageDependencies dependencies = null; if (null == (dependencies = (PackageDependencies) name2dependencies.get(packageName))) { StringOutputHelpers.writeOutput("\tInitializing dependencies for " + packageName, 3); dependencies = new PackageDependencies(); name2dependencies.put(packageName, dependencies); } // initially put the classes in alphabetical order int j = 0; for (; j < dependencies.createFiles.size(); j++) { CreateFile curCreateFile = (CreateFile) dependencies.createFiles.get(j); String curClassName = curCreateFile.getClassFileName(); if (0 > className.compareTo(curClassName)) { dependencies.createFiles.add(j, createFile); break; } } if (j == dependencies.createFiles.size()) { // comes at the end of the list dependencies.createFiles.add(createFile); } } // Now loop through the map and obtain inter package inheritence // dependencies and resolve intra class dependencies. Vector packageNames = new Vector(name2dependencies.keySet()); for (int i = 0; i < packageNames.size();i++) { StringOutputHelpers.writeOutput("\tOrdering files in package " + packageNames.get(i), 3); // For each package PackageDependencies dependencies = (PackageDependencies) name2dependencies.get(packageNames.get(i)); Vector newOrder = new Vector(dependencies.createFiles.size()); for (int j = dependencies.createFiles.size()-1; j >= 0;j--) { // For each classFile in this package // Build the new Vector where the order is by base class CreateFile createFile = (CreateFile) dependencies.createFiles.get(j); // add the dependency to the base class CreateFile baseClass = createFile.getBaseClassCreateFile(); if (null == baseClass) { StringOutputHelpers.writeOutput("\t\tFinished with class " + createFile.getClassFileName(), 3); newOrder.add(0, createFile); continue; } String baseClassPackage = baseClass.getPackageName(); if (baseClassPackage.equals(packageNames.get(i))) { // find where the base class is on the new list and insert after int k = newOrder.size() - 1; done: for (; k >= 0 ; k--) { // go back to top of the chain of base classes. baseClass = createFile.getBaseClassCreateFile(); String baseClassName = baseClass.getClassFileName(); // Compare against each file to find new order CreateFile curCreateFile = (CreateFile) newOrder.get(k); String curClassName = curCreateFile.getClassFileName(); while(true) { if (baseClassName.equals(curClassName)) { newOrder.add(k+1, createFile); break done; } // need to loop up the hierarchy to make sure doesn't // inherit at any depth from curClass CreateFile curBaseClass = baseClass.getBaseClassCreateFile(); if ( null == curBaseClass) { // didn't find any dependency (yet) break; } String curBaseClassName = curBaseClass.getClassFileName(); String curBaseClassPackage = curBaseClass.getPackageName(); if (!curBaseClassPackage.equals(packageNames.get(i))) { // didn't find any dependency (yet) break; } else { baseClass = curBaseClass; baseClassName = curBaseClassName; } } } // end while(true) if (-1 == k) { newOrder.add(0, createFile); } } else { StringOutputHelpers.writeOutput("\tAdding dependency " + baseClass.getPackageName() + " to Package.", 3); dependencies.dependentOn.add(baseClass.getPackageName()); newOrder.add(0, createFile); } StringOutputHelpers.writeOutput("\t\tFinished with class " + createFile.getClassFileName(), 3); } // End for each classFile in this package // see if there is a *_package and put it first. This will print out // the documentation for the package and mark the package for (int j = 0; j < newOrder.size(); j++) { if ( CreateFile.UML_PACKAGE == ((CreateFile) newOrder.get(j)).getFileType() ) { Object packageFile = newOrder.remove(j); newOrder.add(0, packageFile); } } dependencies.createFiles = newOrder; } // End for each package // Now that the classes are ordered in the packages, order // the packages Vector newOrder = new Vector(packageNames.size()); for (int i = 0; i < packageNames.size(); i++) { StringOutputHelpers.writeOutput("\tOrdering package " + packageNames.get(i), 3); PackageDependencies dependencies = (PackageDependencies) name2dependencies.get(packageNames.get(i)); Vector dependentOn = new Vector(dependencies.dependentOn); int j = newOrder.size() - 1; for (; j >= 0; j--) { if (-1 < dependentOn.indexOf(newOrder.get(j))) { newOrder.add(j+1, packageNames.get(i)); break; } } if (-1 == j) { newOrder.add(0, packageNames.get(i)); } } // Now, finally, create the ordered classFiles to return StringOutputHelpers.writeOutput("Original count of files: " + classFiles.size(), 3); classFiles.removeAllElements(); for (int i = 0; i < newOrder.size(); i++) { StringOutputHelpers.writeOutput("\tAdding package " + newOrder.get(i), 3); PackageDependencies dependencies = (PackageDependencies) name2dependencies.get(newOrder.get(i)); classFiles.addAll(dependencies.createFiles); } StringOutputHelpers.writeOutput("Rearranged count of files: " + classFiles.size(), 3); // Special case "MAGEJava" and put it in front of the list for (int i = 0; i < classFiles.size(); i++) { if ( CreateFile.UML_MODEL == ((CreateFile) classFiles.get(i)).getFileType()) { Object mageFile = classFiles.remove(i); classFiles.add(0, mageFile); } } return classFiles; } /** * Description: * Writes the header information out to the file. * *
* @param writer: FileWriter to used to write to the file. * @param header: File for the header information. *
* */ protected void writeHeader( FileWriter writer, File header ) throws Exception { FileReader reader = new FileReader(header); int curChar = -1; writer.write("" + StringOutputHelpers.NEWLINE); reader.close(); } }