/** * * 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.7 $ * */ package org.biomage.tools.generate_classes; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import java.util.TreeMap; import java.util.Set; import java.util.Vector; import org.biomage.tools.helpers.StringOutputHelpers; import org.w3c.dom.*; /** * Description: * Class that is resposible for generating a java file * for the class represented by the class node passed * into the constructor. * */ public class CreateClassFile extends CreateFile { private String id = null; /** * Description: * Constructor for the class file generator. * *

* @param xmiNode: top node beneath which information for * the class can be found. * @param classNode: node representing the class with the information * that can be used to find the attributes and associations. * @param id2classes: map to class nodes for import, base class, * and other info. * @param id2extInfo: map where documentation for the class can be * found. * @param id2packages: map where the package for the class can be * found. * @param id2dataType: map where the datatypes for the attributes * can be found. * @param id2associations: map where the associations for a class * can be found. * @param id2constraint: map where the constraints for an * association end can be found. *

*/ protected CreateClassFile( Element xmiNode, Element classNode, Map id2classes, Map id2extInfo, Map id2packages, Map id2dataType, Map id2associations, Map id2constraint ) throws Exception { super(); // Get the id for the class for use to associate with its package id = classNode.getAttribute("xmi.id"); // Get the base information for the node headerInformation(xmiNode, classNode, id2classes, id2extInfo, id2packages); // There are two 'types' of attributes, simple datatypes // and associations. // Get information on the simple datatype attributes dataAttrInfo = dataAttributeInformation(classNode, id2extInfo, id2dataType); // Get information on the class association attributes associationInfo = associationInformation(classNode, id2classes, id2packages, id2extInfo, id2associations, id2constraint); // finally, get information on the class methods methodInfo = methodInformation(classNode, id2classes, id2dataType, id2packages, id2extInfo); } /** * Description: * Constructor for the class file generator. * *

* @param name: name to give the class. * @param comment: documentation for this class. *

*/ public CreateClassFile( String name, String packageName, String comment, int typeOwned ) throws Exception { super(); className = name; this.packageName = packageName; classDoc = comment; this.typeOwned = typeOwned; } /** * Description: * Default Constructor for the class file generator. * */ public CreateClassFile() throws Exception { super(); } /** * Description: * Obtains the XMI id. * *

* @return the id obtained from the XMI file *

*/ public String getID() throws Exception { return id; } /** * Description: * Obtains the information to generate the information for * the package and the declaration of the class. * *

* @param xmiNode: top node beneath which information for * the class can be found * @param classNode: node representing the class with the information * that can be used to find the attributes and associations. * @param id2classes: map to class nodes for import, base class, * and other info. * @param id2extInfo: map where documentation for the class can be * found * @param id2packages: map where the package for the class can be * found *

*/ protected void headerInformation( Element xmiNode, Element classNode, Map id2classes, Map id2extInfo, Map id2packages ) throws Exception { try { // Obtain the class information className = XMIParseHelpers.getElementName(classNode); StringOutputHelpers.writeOutput("\t" + className, 3); NodeList abstractNode = classNode.getElementsByTagName("Foundation.Core.GeneralizableElement.isAbstract"); if ( !(null == abstractNode || 0 == abstractNode.getLength() || 1 < abstractNode.getLength()) ) { String abstractString = ((Element) abstractNode.item(0)).getAttribute("xmi.value"); if (abstractString.trim().toLowerCase().equals("true")) { isAbstract = true; } else { isAbstract = false; } } // Obtain visibility information for this class NodeList visibilityNode = classNode.getElementsByTagName("Foundation.Core.ModelElement.visibility"); if (null != visibilityNode && null != visibilityNode.item(0)) { visibility = ((Element) visibilityNode.item(0)).getAttribute("xmi.value"); } // Obtain the documentation classDoc = getDocumentation(id2extInfo, classNode.getAttribute("xmi.id")); // Obtain package info CreateMageClassFileList.PackageInformation pi = (CreateMageClassFileList.PackageInformation) id2packages.get(getPackageID(classNode)); if ( null != pi ) { packageName = pi.name; packageDoc = pi.documentation; } // Obtain base class information for this class (will always have one unless it's Extendable) NodeList baseClassNode = classNode.getElementsByTagName("Foundation.Core.Generalization.parent"); NodeList baseClassIDNode = (null == baseClassNode || null == baseClassNode.item(0)? null : ((Element) baseClassNode.item(0)).getElementsByTagName("Foundation.Core.GeneralizableElement")); if (null != baseClassIDNode && null != baseClassIDNode.item(0)) { baseClassID = ((Element) baseClassIDNode.item(0)).getAttribute("xmi.idref"); Element baseNode = (Element) id2classes.get(baseClassID); baseClassName = XMIParseHelpers.getElementName(baseNode); // place on the import list pi = (CreateMageClassFileList.PackageInformation) id2packages.get(getPackageID(baseNode)); if ( null != pi ) { if (!packageName.equals(pi.name)) { packageImports.add(pi.name + "." + getBaseClassFileName()); } } else if (!packageName.equals("Common")) { packageImports.add("Common" + "." + getBaseClassFileName()); } } } catch ( Exception e ) { // for setting a break point for debugging purposes throw e; } } /** * Description: * Adds an attribute to this create file. *

* @param name: name of the attribute. * @param scope: visibility of the attribute. * @param datatype: datatype or enumeration values. * @param comment: optional comment. * @param initialValue: initial value, if any, for this attribute. * @param isRequired: true if the attribute is required. *

*/ public void addAttribute( String name, String scope, String datatype, String comment, String initialValue, boolean isRequired ) throws Exception { if (null == dataAttrInfo) { dataAttrInfo = new Vector(20); } dataAttrInfo.add(new DataTypeAttrInformation(name, scope, datatype, comment, initialValue, isRequired)); } /** * Description: * Inner class to hold datatype attribute information. Checks to see if * the data type is an enum and adjusts the information * accordingly. * */ public class DataTypeAttrInformation extends AttrInformation { String initialValue = null; boolean isRequired = false; /** * Description: * C'tor for class. *

* @param name: name of the attribute. * @param scope: visibility of the attribute. * @param datatype: datatype or enumeration values. * @param comment: optional comment. * @param initialValue: initial value, if any, for this attribute. * @param isRequired: true if the attribute is required. *

*/ protected DataTypeAttrInformation( String name, String scope, String datatype, String comment, String initialValue, boolean isRequired ) throws Exception { super(name, scope, datatype, comment); this.initialValue = initialValue; this.isRequired = isRequired; int index = -1; if (-1 != (index = datatype.indexOf('{'))) { isEnum = true; datatype = datatype.substring(index + 1,datatype.length() - 1); while(-1 != (index = datatype.indexOf(','))) { enumValues.addElement(datatype.substring(0,index).trim()); datatype = datatype.substring(index+1); } enumValues.addElement(datatype); // change the datatype to what will be the inner class name. this.datatype = StringOutputHelpers.initialCap(name); } } /** * Description: * Get method for the initialValue. *

* @return the initialValue, which may be null or have length 0. *

*/ public String getInitialValue() { return initialValue; } /** * Description: * method to determine if the attribute is required. *

* @return returns true if required, false otherwise. *

*/ public boolean isRequired() { return isRequired; } } /** * Description: * Puts together the information on the simple datatypes. * *

* @param xmiNode: top node beneath which information for * the class can be found * @param classNode: node representing the class with the information * that can be used to find the attributes and associations. * @param id2extInfo: map where documentation for the class can be * found *

* *

* @return Vector of AttrInformations *

* */ protected Vector dataAttributeInformation( Element classNode, Map id2extInfo, Map id2dataType ) throws Exception { NodeList attrs = classNode.getElementsByTagName("Foundation.Core.Attribute"); Vector attrInfos = new Vector(); for (int i = 0; i < attrs.getLength(); i++) { Element attrInfoNode = (Element) attrs.item(i); String name = XMIParseHelpers.getElementName(attrInfoNode); Element scopeNode = XMIParseHelpers.getFirstElementByTagName(attrInfoNode, "Foundation.Core.ModelElement.visibility"); String scope = XMIParseHelpers.getElementIDRef(scopeNode); Element typeRefNode = XMIParseHelpers.getAttributeClassifier(attrInfoNode); String datatype = (String) id2dataType.get(XMIParseHelpers.getElementIDRef(typeRefNode)); String comment = getDocumentation(id2extInfo, XMIParseHelpers.getElementID(attrInfoNode)); String initialValue = XMIParseHelpers.getInitialValue(attrInfoNode); boolean isRequired = ( 0 < XMIParseHelpers.getAttributeMultiplicityRangeLower(attrInfoNode)? true : false); attrInfos.addElement(new DataTypeAttrInformation(name, scope, datatype, comment, initialValue, isRequired)); } return attrInfos; } /** * Description: * returns the type of aggregation. * *

* @param endNode: other end association node to check the aggregation * attibute. *

* *

* @return true if aggregated, false otherwise *

* */ protected int typeAggregated( Element endNode ) throws Exception { return XMIParseHelpers.typeAssociationEndAggregate(endNode); } /** * Description: * Puts together the information on the class associations. * Overwrites base class to handle multi-valued assocaitions * *

* @param classNode: node representing this class. * @param id2classes: map to the classes for information for association. * @param id2packages: map to the packages for import information. * @param id2extInfo: map to the comments. * @param id2associations: map to the comments. * @param id2constraint: map to the constraints. *

* *

* @return Vector of ClassAttrInformations *

* */ protected Vector associationInformation( Element classNode, Map id2classes, Map id2packages, Map id2extInfo, Map id2associations, Map id2constraint ) throws Exception { String id = classNode.getAttribute("xmi.id"); Vector associationAttrInfos = new Vector(); Vector assnInfos = (Vector) id2associations.get(id); if (null == assnInfos) { return associationAttrInfos; } for (int i = 0; i < assnInfos.size(); i++) { // Set up the two ends CreateMageClassFileList.AssociationInformation assnInfo = (CreateMageClassFileList.AssociationInformation) assnInfos.elementAt(i); Element associationNode = assnInfo.associationNode; Element thisEndNode = assnInfo.thisEndNode; Element otherEndNode = assnInfo.otherEndNode; // if naviagable from the other end to this end, record the name of the association end // to use in creating dtd container elements. // will need to know this to ignore for dtd and parser generation boolean isOwner = false; Element thisRefNode = XMIParseHelpers.getFirstElementByTagName(thisEndNode, "Foundation.Core.Classifier"); if (XMIParseHelpers.isAssociationEndNavigable(thisEndNode)) { // see if we are owned by the other class int otherOwnership = typeAggregated(otherEndNode); if (XMIParseHelpers.COMPOSITE == otherOwnership) { isOwner = true; } if (XMIParseHelpers.NOT_AGGR == typeOwned) { typeOwned = otherOwnership; } else if (typeOwned != otherOwnership && XMIParseHelpers.NOT_AGGR != otherOwnership) { throw new Exception("Can't be both composite and aggregate in two associations: " + getClassFileName()); } RoleInformation roleInfo = new RoleInformation(XMIParseHelpers.getAssociationEndMultiplicityRangeUpper(thisEndNode), otherOwnership); String roleName = XMIParseHelpers.getElementName(thisEndNode); if (null != roleNames.get(roleName) && !roleInfo.equals(roleNames.get(roleName))) { throw new Exception("Two roles with name " + roleName + " have conflicting cardinality or aggregation."); } roleNames.put(roleName, roleInfo); if (XMIParseHelpers.COMPOSITE != otherOwnership) { isReferenced = true; } } // see if the association is naviagable to the otherEnd if (!XMIParseHelpers.isAssociationEndNavigable(otherEndNode)) { // it's not, so move on to the next one continue; } // We use the name from the other end for the datatype Element otherRefNode = XMIParseHelpers.getFirstElementByTagName(otherEndNode, "Foundation.Core.Classifier"); // Get the name and the package of the other class Element otherClass = (Element) id2classes.get(XMIParseHelpers.getElementIDRef(otherRefNode)); String datatype = XMIParseHelpers.getElementName(otherClass); // Obtain package info CreateMageClassFileList.PackageInformation pi = (CreateMageClassFileList.PackageInformation) id2packages.get(getPackageID(otherClass)); if ( null == pi ) { if (!packageName.equals("Common")) { packageImports.add("Common" + "." + datatype); } } else if ( !packageName.equals(pi.name) ) { packageImports.add(pi.name + "." + datatype); } // get the name of the association end String name = XMIParseHelpers.getElementName(otherEndNode); // At this point, we're going to use an interface to implement the // association end (named for its role). if (interfaceInfo == null) { interfaceInfo = new Vector(); } interfaceInfo.add(CreateInterfaceFile.getInterfaceName(name)); // Append the association's and the other association end's comments String comment = null; String associationComment = getDocumentation(id2extInfo, associationNode.getAttribute("xmi.id")); String associationEndComment = getDocumentation(id2extInfo, otherEndNode.getAttribute("xmi.id")); if (null != associationComment) { comment = associationComment; } if (null != associationComment && null != associationEndComment) { comment += StringOutputHelpers.NEWLINE; } if (null != associationEndComment) { comment += associationComment; } // RMH: 05/21/2002 Changed this to protected so to ease // subclassing in Java. String scope = "protected"; // Get the cardinalities of "the other end" int minCard = XMIParseHelpers.getAssociationEndMultiplicityRangeLower(otherEndNode); int maxCard = XMIParseHelpers.getAssociationEndMultiplicityRangeUpper(otherEndNode); ///////////////////////////////////////////////////////// // Get the cardinalities of "this end" int thisMinCard = XMIParseHelpers.getAssociationEndMultiplicityRangeLower(thisEndNode); int thisMaxCard = XMIParseHelpers.getAssociationEndMultiplicityRangeUpper(thisEndNode); ///////////////////////////////////////////////////////// // Get the navigability of "this end" boolean thisNav = XMIParseHelpers.isAssociationEndNavigable(thisEndNode); // Get the navigability of "other end" boolean otherNav = XMIParseHelpers.isAssociationEndNavigable(otherEndNode); ///////////////////////////////////////////////////////// if ( -1 == maxCard ) { importVector = true; } // find the aggregate info from this end int typeAggregate = typeAggregated(thisEndNode); // find the rank of this association with the other associations for this node String constraint = (String) id2constraint.get(XMIParseHelpers.getElementID(otherEndNode)); int index = -1; if ( null == constraint || -1 == (index = constraint.lastIndexOf(':')) ) { StringOutputHelpers.writeOutput("Bad Constraint: " + constraint, 0); throw new Exception("Association end doesn't specify its rank: " + XMIParseHelpers.getElementID(otherEndNode)); } Integer rank = new Integer(constraint.substring(index+1).trim()); // and find out if the association should be ordered boolean isOrdered = false; if (XMIParseHelpers.isAssociationEndOrdered(otherEndNode) || (-1 != constraint.indexOf("ordered") && -1 == constraint.indexOf("unordered"))) { isOrdered = true; } //////////////////////////////////////////////////////////// associationAttrInfos.addElement(new AssociationAttrInformation(name, scope, datatype, comment, minCard, maxCard, thisMinCard, thisMaxCard, typeAggregate, isOrdered, rank, isOwner, XMIParseHelpers.isIdentifiable(otherEndNode), thisNav, otherNav)); } /////////////////////////////////////////////////////////////// // mm 5/22/02 Do the ordering here of associations so platforms can depend // on model ordering out of the box. // Order the vector by rank of each association Map rank2assns = new TreeMap(); for (int i = 0; i < associationAttrInfos.size(); i++) { // NOTE: two associations can have the same rank, they will // form a choice group in the content AssociationAttrInformation assnInfo = (AssociationAttrInformation) associationAttrInfos.get(i); Integer rank = assnInfo.getRank(); Vector assns = (Vector) rank2assns.get(rank); if (null == assns) { assns = new Vector(); } assns.add(assnInfo); rank2assns.put(rank, assns); } associationAttrInfos.clear(); Iterator iter = rank2assns.values().iterator(); // Flatten out the list again while (iter.hasNext()) { Vector assns = (Vector) iter.next(); for (int i = 0; i < assns.size(); i++) { associationAttrInfos.add(assns.get(i)); } } return associationAttrInfos; } /** * Description: * Inner class to hold method information. * */ public class MethodInformation extends AttrInformation { protected String semantics; protected ParamInfo returnType; protected Vector paramInfos = new Vector(10); protected String preconditions; protected String postconditions; /** * Description: * Inner class to hold parameter information. * */ public class ParamInfo extends AttrInformation { protected String kind = null; protected String defaultValue = null; // Not used currently /** * Description: * C'tor for class. *

* @param paramNode: the node to get the information on the method. * @param id2classes: map to the classes for information for parameters. * @param id2dataType: map to the datatypes for parameters. * @param id2packages: map to the packages for import information. * @param id2extInfo: map to the comments. *

*/ protected ParamInfo( Element paramElement, Map id2classes, Map id2dataType, Map id2packages, Map id2extInfo ) throws Exception { // Set the name and the comment but have to delay setting the datatype super(XMIParseHelpers.getElementName(paramElement), "public", null, getDocumentation(id2extInfo, XMIParseHelpers.getElementID(paramElement))); kind = XMIParseHelpers.getParameterKind(paramElement); // The parameter type will either be another class or a data type String parameterTypeID = XMIParseHelpers.getParameterTypeID(paramElement); Element classElement = (Element) id2classes.get(parameterTypeID); if (null != classElement) { datatype = XMIParseHelpers.getElementName(classElement); } else // look up as datatype { datatype = (String) id2dataType.get(parameterTypeID); if (null == datatype) { throw new Exception("CreateClassFile.methodInformation(): can't find parameter type"); } } } } /** * Description: * C'tor for class. *

* @param operationNode: the node to get the information on the method. * @param id2classes: map to the classes for information for parameters. * @param id2dataType: map to the datatypes for parameters. * @param id2packages: map to the packages for import information. * @param id2extInfo: map to the comments. *

*/ protected MethodInformation( Element operationElement, Map id2classes, Map id2dataType, Map id2packages, Map id2extInfo ) throws Exception { // Set the name and the comment but have to delay setting the datatype super(XMIParseHelpers.getElementName(operationElement), "public", null, getDocumentation(id2extInfo, XMIParseHelpers.getElementID(operationElement))); semantics = XMIParseHelpers.getOperationSpecification(operationElement); NodeList parameters = XMIParseHelpers.getParameterElements(operationElement); for (int i = 0; i < parameters.getLength(); i++) { ParamInfo paramInfo = new ParamInfo((Element) parameters.item(i), id2classes, id2dataType, id2packages, id2extInfo); if ("return".equals(paramInfo.kind)) { returnType = paramInfo; // now set the datatype datatype = paramInfo.datatype; } else { paramInfos.add(paramInfo); } } preconditions = null; // not used postconditions = null; // not used } /** * Description: * Get method for the name. *

* @return the name. *

*/ public String getName() { return name; } /** * Description: * Get method for the comment. *

* @return the comment. *

*/ public String getComment() { return comment; } /** * Description: * Get method for the returnType. *

* @return the returnType. *

*/ public ParamInfo getReturnType() { return returnType; } /** * Description: * Get method for the semantics. *

* @return the semantics. *

*/ public String getSemantics() { return semantics; } /** * Description: * Get method for the parameters. *

* @return the parameters. *

*/ public Vector getParamInfos() { return paramInfos; } /** * Description: * Get method for the preconditions. *

* @return the preconditions. *

*/ public String getPreconditions() { return preconditions; } /** * Description: * Get method for the postconditions. *

* @return the postconditions. *

*/ public String getPostconditions() { return postconditions; } } /** * Description: * Puts together the information on the class methods. * *

* @param classNode: node representing this class. * @param id2classes: map to the classes for information for parameters. * @param id2dataType: map to the datatypes for parameters. * @param id2packages: map to the packages for import information. * @param id2extInfo: map to the comments. *

* *

* @return Vector of ClassAttrInformations *

* */ protected Vector methodInformation( Element classNode, Map id2classes, Map id2dataType, Map id2packages, Map id2extInfo ) throws Exception { Vector methodInfo = new Vector(10); NodeList operations = XMIParseHelpers.getOperationElements(classNode); for (int i = 0; i < operations.getLength(); i++) { methodInfo.add(new MethodInformation((Element) operations.item(i), id2classes, id2dataType, id2packages, id2extInfo)); } return methodInfo; } /** * Description: * Returns what kind of model element this class is based on. * * @return returns that this represents the model itself */ public int getFileType() { return UML_CLASS; } }