// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.generator; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.*; /** * GeneratorBase is the base class for code generation classes in AdkGen. It handles the * reusable elements of code generation of the ADK classes, while subclasses implement the * language-specific elements * * * @author Andy Elmhorst * @version 1.0 */ public abstract class CodeGenerator extends Generator { protected CodeGenerator( String srcDir, String destDir ) { super( srcDir, destDir ); } /** * Writes a single ElementDef const line to the specified SDO Library class * @param out * @param o */ protected abstract void writeElementDefConst(PrintWriter out, String commentName, String constName ); protected abstract void writeToStringOverride(PrintWriter out, FieldDef def); protected abstract void writeAliasDefinition(PrintWriter out, String elementDefName, String flags, String aliasVer, String renderAs, String sequence ); protected abstract void writeEnumClass(PrintWriter out, EnumDef enumDef); protected abstract void writeSDOLibraryHeader(PrintWriter out, String className); protected abstract void writeClassHeader( PrintWriter writer, ObjectDef def); protected abstract void writeClassComment( PrintWriter writer, ObjectDef def); protected abstract void writeClassCtor( PrintWriter writer, ObjectDef def ); protected abstract void writeAbstractMethods( PrintWriter writer, ObjectDef def); protected abstract void writeSIFDTDClass(DB database, String dir, DB packageDB) throws IOException; protected abstract void writeElementDefCreationLine( PrintWriter out, String dtdSymbol, String parentDtdSymbol, boolean useElementDefAlias,String fieldName, String renderName, int sequenceNumber, FieldType classType, String localPackage, SIFVersion earliestVersion, SIFVersion latestVersion, String flags, String typeConverter ); protected abstract String getTypeConverterName( FieldType fieldType ); protected abstract String getADKSimpleType( ADKDataType dataType ); protected abstract void writeComplexField( PrintWriter writer, ObjectDef objectDef, FieldDef field, ObjectDef def) throws GeneratorException; protected abstract void writeSimpleField( PrintWriter writer, ObjectDef objectDef, FieldDef field ); protected abstract void writeDTDHeader( PrintWriter out, DB db, String pkg ); protected abstract void writeDtdLoad( PrintWriter out ); protected abstract void writeDTDTableUpdates( PrintWriter out, DB db, String pkg ); protected abstract void writeEnumClassComment( PrintWriter out, EnumDef enumDef ); protected abstract void writeEnumHeader( PrintWriter out, EnumDef enumDef ); protected abstract void writeDTDClassComment( PrintWriter out, DB db ) throws IOException; protected abstract void writeDTDAbstractMethods( PrintWriter out ); protected abstract String symbol(String s); protected abstract String getSuperClassSeperatorAndName( ObjectDef superClass ); /** * Generate Java classes based on the information in the DB */ public void generate(DB[] dbs) throws IOException, GeneratorException, MergeException { super.sortAndGenerateObjects( dbs ); // Now generate the DTD classes System.out.println("\r\nGenerating DTD classes..."); generateDTDClasses(dbs); } protected static String[] InfraMessages = { "SIF_Ack", "SIF_Event", "SIF_Provide", "SIF_Register", "SIF_Request", "SIF_Response", "SIF_Subscribe", "SIF_SystemControl", "SIF_Unprovide", "SIF_Unregister", "SIF_Unsubscribe", "SIF_ZoneStatus", "SIF_Provision" }; /** * Generates the SIFDTD class, which extends openadk.library.DTD * to provide information about the core SIF data type definition. In * addition, generates a derived class for each version of SIF, where * the name is the version (e.g."SIF10r1"). * * @param dbs An array of DB objects sorted by SIF version number */ protected void generateDTDClasses(DB[] databases) throws IOException, GeneratorException { String dir = fDir+"openadk/library"; // Use the latest database for retrieving package names DB packageDB = databases[ databases.length - 1 ]; PrintWriter out; writeSIFDTDClass(databases[0], dir, packageDB); // // Create SDOLibrary.java classes... // for( String sdoName : packageDB.getDefinitionFileKeysSet() ) { // Generate the package-specific SDOLibrary classes... String packageDir = fDir+"openadk/library/" + sdoName; String packageSDOName = sdoName.substring( 0 , 1 ).toUpperCase() + sdoName.substring( 1 ) + "DTD"; File md = new File(packageDir); md.mkdirs(); String packageFn = packageDir + File.separator + packageSDOName + getFileExtension() ; System.out.println("- Generating: "+ packageFn ); out = null; try { out = new PrintWriter(new OutputStreamWriter(new FileOutputStream( packageFn ), "utf-8"),true ); writeDTDHeader(out, packageDB ,sdoName); writeDTDClassComment(out, packageDB ); writeSDOLibraryHeader(out, packageSDOName ); // Write out the ElementDef statics for each object ObjectDef[] o = databases[0].getObjects(); // Sort them Arrays.sort( o, new Comparator<ObjectDef>() { public int compare( ObjectDef o1, ObjectDef o2 ) { return o1.getDTDSymbol().compareTo( o2.getDTDSymbol() ); } } ); // Write the ElementDef constants to the DTD class writeDTDConstants( out, sdoName, o ); // Write the load() method on the DTD class writeDTDLoad(out, sdoName, packageDB, o); out.println(); // Write the addElementMappings method on the DTD class writeDTDTableUpdates( out, databases[0], sdoName ); writeClassFooter(out); } finally { if( out != null ) { try { out.close(); } catch( Exception ex ) { } } } } // Generate the Enumeration classes System.out.println("\r\nGenerating enum classes..."); writeEnumClasses(dir, databases[0].getEnums() ); } private void writeEnumClasses(String dir, EnumDef[] enums) throws IOException { String fn; PrintWriter out; for( int i = 0; i < enums.length; i++ ) { File md = new File( dir + File.separator + enums[i].fPackage ); md.mkdirs(); fn = dir + File.separator + enums[i].fPackage+File.separator+enums[i].getName()+ getFileExtension(); System.out.println("- Generating: "+fn); out = null; try { out = new PrintWriter(new OutputStreamWriter(new FileOutputStream( fn ), "utf-8"),true ); writeEnumHeader(out,enums[i]); writeEnumClassComment(out,enums[i]); writeEnumClass(out, enums[i]); } finally { if( out != null ) { try { out.close(); } catch( Exception e ) { } } } } } /** * Write the load() method on the specified DTD class * @param out * @param sdoName * @param db * @param o */ private void writeDTDLoad(PrintWriter out, String sdoName, DB db, ObjectDef[] o) { // Write out the initialize() method out.println(); writeDtdLoad( out ); out.println("\t{"); out.println("\t\t// Objects defined by this SDO Library..."); out.println(); Vector<ObjectDef> sdoObjects = new Vector<ObjectDef>(); for( int k = 0; k < o.length; k++ ) { boolean trap = o[k].getName().equalsIgnoreCase("SIF_LogEvent"); if( ( o[k].getFlags() & ObjectDef.FLAG_NO_SIFDTD ) == 0 ) { if( !o[k].getLocalPackage().equals( sdoName ) ) { if( trap ) System.out.println("************1"); continue; } sdoObjects.addElement(o[k]); String typeConverter = null; FieldType ft = o[k].getValueType(); if( ft != null ){ typeConverter = getTypeConverterName( ft ); } writeElementDefCreationLine( out, o[k].getDTDSymbol(), null, false, o[k].getName(), o[k].getRenderAs(), o[k].getSequenceOverride() == -1 ? 0 : o[k].getSequenceOverride(), o[k].getValueType(), o[k].getLocalPackage(), o[k].getEarliestVersion(), o[k].getLatestVersion(), ( o[k].isTopic() ? "ElementDefImpl.FD_OBJECT":"" ), typeConverter ); // Write out any aliases Map<String, List<SIFVersion>> aliases = o[k].getAliases(); if( aliases != null ) { for( Map.Entry<String, List<SIFVersion>> entry : aliases.entrySet() ) { SIFVersion aliasVer = entry.getValue().get( 0 ); StringBuffer buf = new StringBuffer(); buf.append( aliasVer.getMajor() ); buf.append( aliasVer.getMinor() ); if( aliasVer.getRevision() > 0 ){ buf.append( "r" ); buf.append( aliasVer.getRevision() ); } //LibraryDTD.TRANSACTIONLIST_TRANSACTION.defineVersionInfo(SIFVersion.SIF20, "Transaction", 1, (ElementDefImpl.FD_REPEATABLE)); // (SIF 20 alias) writeAliasDefinition(out, o[k].getDTDSymbol(), "0", buf.toString(), entry.getKey(), "0" ); } } } } out.println(); // Write out a static ElementDef defining each SIF element for( int k = 0; k < sdoObjects.size(); k++ ) { ObjectDef oo = (ObjectDef)sdoObjects.elementAt(k); out.println(); out.println("\t\t// <" + ( oo.getRenderAs() == null ? oo.getName() : oo.getRenderAs() ) + "> fields (" + oo.getAllFields().length + " entries)" ); FieldDef[] fields = oo.getDTDFields(); for( int f = 0; f < fields.length; f++ ) { if( ( fields[f].getFlags() & ObjectDef.FLAG_NO_SIFDTD ) == 0 ) { String flags = getFieldFlags( fields[f].getFlags() ); String fieldClassType = fields[f].getFieldType().getClassType(); ObjectDef fod = db.getObject( fieldClassType ); boolean useElementDefAlias = fields[f].isComplex() && !fieldClassType.equals(fields[f].getName()); // if( db.getVersion().compareTo( fields[f].getEarliestVersion() ) >= 0 ) { String packageName = (fod == null ? oo.getLocalPackage() : fod.getLocalPackage()); String typeConverterName = null; if( !fields[f].isComplex() ){ typeConverterName = getTypeConverterName( fields[f].getFieldType() ); } else if ( fod != null ) { FieldType objSimpleType = fod.getValueType(); if( objSimpleType != null ){ typeConverterName = getTypeConverterName( objSimpleType ); } } writeElementDefCreationLine( out, fields[f].getElementDefConst( this ), oo.getDTDSymbol(), useElementDefAlias, fields[f].getName(), fields[f].getElementDefExpression(), fields[f].getSequence(), fields[f].getFieldType(), packageName, fields[f].getEarliestVersion(), fields[f].getLatestVersion(), flags.toString(), typeConverterName ); // Write out any aliases Set<Alias> aliases = fields[f].getAliases(); if( aliases != null ) { for(Alias alias : aliases) { SIFVersion aliasVer = alias.getVersion(); StringBuffer buf = new StringBuffer(); buf.append( aliasVer.getMajor() ); buf.append( aliasVer.getMinor() ); if( aliasVer.getRevision() > 0 ){ buf.append( "r" ); buf.append( aliasVer.getRevision() ); } writeAliasDefinition( out, fields[f].getElementDefConst( this ), getFieldFlags( alias.getFlags() ), buf.toString(), alias.getElementDefExpression(), String.valueOf( alias.getSequence() ) ); } } } } } if( oo.isTopic() ) { //latestVersion may not be the latest one supported. ie, if you want to do a build of the old dm, or if .net is behind/ahead of java in terms of sif version support SIFVersion latestVersion = SIFVersion.getEarliest(1); for ( DB dbInList : Main.self.fDBs.values() ) { if ( dbInList != null && dbInList.fVersion.compareTo(latestVersion) > 0 ) latestVersion = dbInList.fVersion; } writeElementDefCreationLine( out, oo.getDTDSymbol() + "_SIF_EXTENDEDELEMENTS", oo.getDTDSymbol(), false, "SIF_ExtendedElements", null, 127, null, (Main.self.fLanguage.equals("cs")? "global" : "common"), SIFVersion.SIF15r1, latestVersion, "0", null ); writeElementDefCreationLine( out, oo.getDTDSymbol() + "_SIF_METADATA", oo.getDTDSymbol(), false, "SIF_Metadata", null, 128, null, "datamodel", SIFVersion.SIF20, latestVersion, "0", null ); } } out.println("\t}"); } /** * Write the element def constants for the specified library class * @param out * @param sdoName * @param o */ private void writeDTDConstants(PrintWriter out, String sdoName, ObjectDef[] o) { // Write out the public constants defined by this package int dtdItemCount = 2; for( int k = 0; k < o.length; k++ ) { if( !o[k].getLocalPackage().equals( sdoName ) ){ continue; } if( ( o[k].getFlags() & ObjectDef.FLAG_NO_SIFDTD ) == 0 ) { writeElementDefConst(out, "Defines the <" + o[k].getName()+"> SIF Data Object", o[k].getDTDSymbol() ); dtdItemCount++; } } out.println(); String comment = null; for( int k = 0; k < o.length; k++ ) { if( !o[k].getLocalPackage().equals( sdoName ) ){ continue; } out.println(); out.println("\t// Field elements of "+o[k].getDTDSymbol() + " (" + o[k].getAllFields().length + " fields)" ); FieldDef[] fields = o[k].getAllFields(); for( int f = 0; f < fields.length; f++ ) { if( ( fields[f].getFlags() & FieldDef.FLAG_ATTRIBUTE ) != 0 ) { comment = fields[f].getName() + " attribute"; } else comment = "<" + fields[f].getName() + "> element"; if( ( fields[f].getFlags() & FieldDef.FLAG_NO_SIFDTD ) == 0 ) { writeElementDefConst( out, "Defines the " + comment + " as a child of <" + o[k].getName() + ">", fields[f].getDTDSymbol() ); dtdItemCount++; } } if( o[k].isTopic() ) { writeElementDefConst( out, "SIF 1.5 and later: Defines the built-in SIF_ExtendedElements element common to all SIF Data Objects", o[k].getDTDSymbol() + "_SIF_EXTENDEDELEMENTS" ); writeElementDefConst( out, "SIF 2.0 and later: Defines the built-in SIF_Metadata element common to all SIF Data Objects", o[k].getDTDSymbol() + "_SIF_METADATA" ); dtdItemCount+=2; } } } private String getFieldFlags( int flags ) { StringBuffer strFlags = new StringBuffer(); if( ( flags & FieldDef.FLAG_ATTRIBUTE ) != 0 ) { strFlags.append("ElementDefImpl.FD_ATTRIBUTE"); } else if( ( flags & FieldDef.FLAG_COMPLEX ) == 0) { strFlags.append("ElementDefImpl.FD_FIELD"); } if( ( flags & FieldDef.FLAG_DO_NOT_ENCODE ) != 0 ) { if( strFlags.length() > 0 ) strFlags.append('|'); strFlags.append("ElementDefImpl.FD_DO_NOT_ENCODE"); } if( ( flags & FieldDef.FLAG_COLLAPSED ) != 0 ) { if( strFlags.length() > 0 ) strFlags.append('|'); strFlags.append("ElementDefImpl.FD_COLLAPSE"); } if( (flags & FieldDef.FLAG_DEPRECATED) != 0 ) { if( strFlags.length() > 0 ) strFlags.append('|'); strFlags.append("ElementDefImpl.FD_DEPRECATED"); } if( ( flags & FieldDef.FLAG_REPEATABLE ) != 0 ) { if( strFlags.length() > 0 ) strFlags.append('|'); strFlags.append("ElementDefImpl.FD_REPEATABLE"); } return strFlags.toString(); } protected String an(String str, boolean upperCase, boolean code) { StringBuffer b = new StringBuffer(); char ch = Character.toLowerCase(str.charAt(0)); if( ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' || ch == 'h' ) b.append( upperCase ? "An":"an" ); else b.append( upperCase ? "A":"a" ); b.append(" "); if( code ) b.append("<code>"); b.append(str); if( code ) b.append("</code>"); return b.toString(); } protected String plural(String str) { if( str.endsWith("s") ) return str+"es"; return str+"s"; } protected void writeExtras(PrintWriter out, ObjectDef o) throws IOException { writeExtras( out, o.getExtrasFile() ); } protected void writeExtras(PrintWriter out, String fn) throws IOException { if( fn != null ) { BufferedReader in = null; try { in = new BufferedReader( new FileReader(fn) ); out.println("\r\n// BEGIN EXTRA METHODS ("+fn.replace('\\', '/')+")\r\n"); do { String s = in.readLine(); out.println(s); System.out.println(s); } while( in.ready() ); out.println("\r\n// END EXTRA METHODS\r\n"); } finally { if( in != null ) { try { in.close(); } catch( IOException ignored ) { } } } } } protected void writeFileHeader(PrintWriter out) { out.println("// THIS FILE WAS AUTO-GENERATED BY ADKGEN -- DO NOT MODIFY!"); out.println(); out.println("//"); out.println("// Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s)."); out.println("// All rights reserved."); out.println("//"); out.println(); } /** * Generates a CSharp class file for an ObjectDef */ protected void generateObject(ObjectDef o) throws IOException, GeneratorException { if ( this instanceof JavaGenerator && "SIF_ExtendedElement".equals( o.fName ) ) { return; } String dir = fDir+"openadk.library."+ toProperCase( o.getLocalPackage() ); dir = dir.replace('.',File.separatorChar); String fn = dir+File.separator+o.getName()+ getFileExtension(); System.out.println("- Generating: "+fn); PrintWriter out = null; try { File md = new File(dir); md.mkdirs(); out = new PrintWriter( new OutputStreamWriter(new FileOutputStream( fn ), "utf-8"),true ); writeClassHeader(out,o); writeClassCtor(out,o); writeAbstractMethods(out,o); // If the object has a simple content model, write out an accessor // for the element content. This will consist of a single property // name "Value" that gets and sets the text value of the element. if( o.getName().equals( "StringElement" ) ){ System.out.println( "Break" ); } FieldDef value = o.getValueDef( this ); if( value != null ){ writeSimpleField( out, o, value ); } // Now write out getter/setter methods for each FieldDef. If the // FieldDef is a complex object, multiple getters/setters may be // written. If the FieldDef is a simple string-type field, only // one set of getter/setter is written. // FieldDef[] fields = o.getAllFields(); ADKElementType elementType = o.getElementType( fDB, this ); for( int i = 0; i < fields.length; i++ ) { if( fields[i].isComplex() ) { ObjectDef ref = null; String classType = fields[i].getFieldType().getClassType(); ref = fDB.getObject( classType ); if( ref == null ) { throw new GeneratorException( fields[i].getName()+ " references object type \"" + classType + "\", but that object type is not defined" ); } else { writeComplexField(out,o,fields[i],ref); } } else{ writeSimpleField(out,o,fields[i]); } } // Special Case: If the element isEmpty and has only one attribute, // override the toString() method to return that attribute's // value (e.g. <StatePr Code="UT"/> falls into this category.) // if( o.isEmpty() ) { FieldDef[] req = o.getMandatoryFields( this ); if( req != null && req.length == 1 ) { FieldDef def = req[0]; writeToStringOverride(out, def); } } try { writeExtras(out,o); } catch( Exception e ) { throw new GeneratorException("Could not copy extras file for object "+o.getName()+": " + e ); } writeClassFooter(out); } finally { if( out != null ) { try { out.close(); } catch( Exception e ) { } } } } protected void writeClassFooter(PrintWriter out) { out.println("}"); } /** * @param m * @return Returns an array of strings. The first element is the data type that should be passed in * to the constructor. The second element is the argument name to use. The third element is a block of code that creates the actual datatype to set * in the parent object. */ /* Unused Code protected String[] getConstructorParts( FieldDef m ) { String[] returnVal = null; ObjectDef paramType = fDB.getObject(m.getName()); if( paramType != null && paramType.getFields().length == 1 && !paramType.hasValueText() ) { FieldDef[] mandatoryFields = paramType.getMandatoryFields(); if( mandatoryFields.length == 1 && mandatoryFields[0].getEnum() == null ) { returnVal = getConstructorParts( mandatoryFields[0] ); if( returnVal == null ) { returnVal = new String[3]; returnVal[0] = mandatoryFields[0].getClassType(); returnVal[1] = toArgument( mandatoryFields[0].getName() ); if( mandatoryFields[0].getClassType().equals("String")) { returnVal[2] = returnVal[1]; } else { returnVal[2] = " new " + m.getClassType()+"( " + returnVal[1] + " ) " ; } } else { returnVal[2] = " new " + m.getClassType()+"( " + returnVal[2] + " ) " ; } } return returnVal; } return null; } protected abstract String toArgument( String src ); */ protected String toArgument( FieldDef m ) { ObjectDef paramType = fDB.getObject(m.getName()); if( paramType != null && paramType.getAllFields().length == 1 && paramType.getValueDef( this ) == null ) { FieldDef[] mandatoryFields = paramType.getMandatoryFields( this ); if( mandatoryFields.length == 1 ){ if( mandatoryFields[0] == m || toArgument(mandatoryFields[0]) == null ){ return mandatoryFields[0].getFieldType().getClassType(); } } } return null; } protected abstract String toProperCase(String s); protected abstract String getFileExtension(); protected void writeDTDTableUpdatesForObject( PrintWriter out, DB db, ObjectDef obj, HashSet<String>alreadyAdded ) { String objName = obj.getName(); if( objName.equals( "Demographics" ) ){ System.out.println("Ready to Break;"); } SortedSet<TagItem> tags = new TreeSet<TagItem>(); addAliases( db, obj, objName, tags, obj.getLatestVersion(), null ); for( TagItem tagItem : tags ){ if( alreadyAdded.contains( tagItem.fTag ) ){ throw new RuntimeException( tagItem.fTag + " is already defined." ); } alreadyAdded.add( tagItem.fTag ); writeSingleDTDTableUpdate( out, tagItem.fTag, tagItem.fElementDefConst, tagItem.fComment ); } } private void addAliases( DB db, ObjectDef obj, String parentObjName, Set<TagItem> addTo, SIFVersion maxVersion, String comment ) { for( FieldDef field : obj.getAllFields() ){ if( (field.getFlags() & FieldDef.FLAG_NO_SIFDTD ) != 0 || (field.getFlags() & FieldDef.FLAG_TEXT_VALUE ) != 0 ){ continue; } if( parentObjName.equals( "Demographics" ) && field.getName().equals( "RaceList" ) ){ System.out.println( "Ready to Break" ); } String elementDefConst = field.getElementDefConst( this ); String renderAs = field.getRenderAs(); if( renderAs == null ){ renderAs = field.getName(); } addTo.add( new TagItem( parentObjName + "_" + renderAs, elementDefConst, comment ) ); if( ( field.getFlags() & FieldDef.FLAG_COLLAPSED ) != 0 ){ ObjectDef collapsedContainer = db.getObject( field.getFieldType().getClassType() ); addAliases( db, collapsedContainer, parentObjName, addTo, field.getEarliestVersion(), "Collapsed in " + field.getEarliestVersion() ); } Set<Alias> aliases = field.getAliases(); if( aliases != null ){ for( Alias alias : aliases ) { SIFVersion version = alias.getVersion(); if( version.compareTo( maxVersion ) <= 0 ){ if( ( alias.getFlags() & FieldDef.FLAG_COLLAPSED ) != 0 ){ ObjectDef collapsedContainer = db.getObject( field.getFieldType().getClassType() ); addAliases( db, collapsedContainer, parentObjName, addTo, version, "Collapsed in " + version ); } else { TagItem item = new TagItem( parentObjName + "_" + alias.getTag(), elementDefConst, version.toString() + " alias" ); addTo.add( item ); } } } } } } protected abstract void writeSingleDTDTableUpdate(PrintWriter out, String tagCombination, String dtdConstName, String addComment); private static class TagItem implements Comparable<TagItem> { private String fTag; private String fComment; private String fElementDefConst; public TagItem( String tag, String elementDefConst, String comment ) { fTag = tag; fComment = comment; fElementDefConst = elementDefConst; } public int compareTo( TagItem tag2 ){ return fTag.compareTo( tag2.fTag ); } @Override public boolean equals(Object arg0) { if( arg0 == null ){ return false; } if( !(arg0 instanceof TagItem) ){ return false; } return fTag.equals(((TagItem)arg0).fTag); } @Override public int hashCode() { return fTag.hashCode(); } } }