// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.generator; import java.util.Collection; import java.util.Enumeration; import java.util.Hashtable; import java.util.Set; /** * Metadata database for a version of the SIF Specification. */ public class DB { protected SIFVersion fVersion; protected String fNamespace; protected Hashtable<String, EnumDef> fEnums = new Hashtable<String, EnumDef>(); private Hashtable<String, ObjectDef> fObjects = new Hashtable<String, ObjectDef>(); private Hashtable<String, DefinitionFile> fDefinitionFiles = new Hashtable<String, DefinitionFile>(); protected ObjectDef fSIFTimeDef; public DB( SIFVersion version, String namespace ) { fVersion = version; fNamespace = namespace; try { // Create an ObjectDef that will be used for any instances of SIFTime fSIFTimeDef = new ObjectDef( -99999, "SIFTime", "common", SIFVersion.SIF11 ); FieldDef zone = fSIFTimeDef.defineAttr( "Zone", "String" ); zone.setFlags( "R" ); zone.setDesc( "The Zone attribute describes the time zone as an offset from the zero meridian (e.g <code>\"UTC-08:00\"</code> )." ); } catch( ParseException parseEx ){ System.out.println( parseEx ); } } /** * Merge the definitions of this DB into another DB */ public void mergeInto( DB target ) throws MergeException { System.out.println("Merging metadata for \"" + fVersion.toString() + "\" into \"" + target.getVersion().toString() + "\"..." ); mergeEnums( target ); mergeObjectDefs( target ); } /** * Merge all of the object defs from the target DB into this DB * @param mergeTargetDB * @throws MergeException */ private void mergeObjectDefs(DB mergeTargetDB) throws MergeException { System.out.println("- Processing object definitions..."); for( Enumeration e = fObjects.keys(); e.hasMoreElements(); ) { String key = (String)e.nextElement(); ObjectDef mergeSourceObjectDef = (ObjectDef)fObjects.get( key ); ObjectDef mergeTargetObjectDef = (ObjectDef)mergeTargetDB.fObjects.get(key); // if( key.equals( "MediumOfInstruction") ){ // System.out.println( "Break Time"); // } if( mergeTargetObjectDef == null ) { // Add the missing ObjectDef to the target System.out.println( " (+) \""+key+"\" (" + mergeSourceObjectDef.getAllFields().length + " fields) not found in target; adding"); mergeTargetDB.fObjects.put( key, mergeSourceObjectDef ); } else { System.out.println("Object "+mergeTargetObjectDef.getName()+" now has SIFVersion range of "+mergeTargetObjectDef.getEarliestVersion()+" - "+fVersion); mergeTargetObjectDef.setLatestVersion( fVersion ); // Do some sanity checking if( !mergeTargetObjectDef.fPackage.equals( mergeSourceObjectDef.fPackage ) ) throw new MergeException("Target and source have different package values (target=\""+mergeTargetObjectDef.getName()+"\", package=\""+mergeTargetObjectDef.fPackage+"\", source=\""+mergeSourceObjectDef.getName()+"\", package=\""+mergeSourceObjectDef.fPackage+"\""); if( !mergeTargetObjectDef.fSuperclass.equals( mergeSourceObjectDef.fSuperclass ) ) throw new MergeException("Target and source have different superclass values (target=\""+mergeTargetObjectDef.getName()+"\", superclass=\""+mergeTargetObjectDef.fSuperclass+"\", source=\""+mergeSourceObjectDef.getName()+"\", superclass=\""+mergeSourceObjectDef.fSuperclass+"\""); // Append this fExtrasFile to the target's if necessary if( mergeSourceObjectDef.fExtrasFile != null ) { System.out.println( " (+) \""+key+"\" has an Extras File; adding"); if( mergeTargetObjectDef.fExtrasFile == null ){ mergeTargetObjectDef.fExtrasFile = mergeSourceObjectDef.fExtrasFile; } else { throw new IllegalStateException( "ADKGen does not yet support multiple Extras files."); //existingObjectDef.fExtrasFile = existingObjectDef.fExtrasFile + ";" + thisObjectDef.fExtrasFile; } } // Determine if the object's key fields (required elements and // attributes) differ StringBuffer keyCmp1s = new StringBuffer(); FieldDef[] keyCmp1 = mergeSourceObjectDef.getKey( null ); for( int n = 0; n < keyCmp1.length; n++ ) { keyCmp1s.append( keyCmp1[n].getName() == null ? "null" : keyCmp1[n].getName() ); if( n != keyCmp1.length-1 ) keyCmp1s.append('+'); } StringBuffer keyCmp2s = new StringBuffer(); FieldDef[] keyCmp2 = mergeTargetObjectDef.getKey( null ); for( int n = 0; n < keyCmp2.length; n++ ) { keyCmp2s.append( keyCmp2[n].getName() == null ? "null" : keyCmp2[n].getName() ); if( n != keyCmp2.length-1 ) keyCmp2s.append('+'); } if( !keyCmp1s.toString().equals(keyCmp2s.toString()) ) throw new MergeException("\""+key+"\" target and source have different key signature; merge not yet supported by adkgen." + " Target=\"" + keyCmp2s.toString() + "\"; Source=\"" + keyCmp1s.toString() + "\"" ); mergeTargetObjectDef.setLatestVersion( getVersion() ); // Update the description if there is an updated description if( mergeSourceObjectDef.fDesc != null && mergeSourceObjectDef.fDesc.length() > 0 ){ mergeTargetObjectDef.setDesc( mergeSourceObjectDef.fDesc ); } if( !(mergeTargetObjectDef.getTag().equals( mergeSourceObjectDef.getTag() ))){ System.out.println( " (~) Object has different tag; adding alias"); System.out.println( " "+mergeTargetDB.getVersion()+" -> " + mergeTargetObjectDef.getTag() ); System.out.println( " "+this.getVersion()+" -> " + mergeSourceObjectDef.getTag() ); mergeTargetObjectDef.addAlias( fVersion, mergeSourceObjectDef.getTag() ); } mergeFieldDefs( mergeSourceObjectDef, mergeTargetObjectDef, this.getVersion(), key ); } } } /** * Merges a set of Field Defs from a new version of SIF into a target DB that represents an older version of SIF * * @param mergeSource An Object Def from a more recent version of SIF * @param mergeTarget An object Def from a DB object representing a prior version of SIF * @param objectKey * @param target * @throws MergeException */ private void mergeFieldDefs(ObjectDef mergeSource, ObjectDef mergeTarget, SIFVersion mergeVersion, String objectKey) throws MergeException { // Determine if any of the object's fields differ in their definition for( String fieldKey : mergeSource.fFields.keySet() ) { FieldDef mergeSourceField = (FieldDef)mergeSource.fFields.get( fieldKey ); FieldDef mergeTargetField = (FieldDef)mergeTarget.fFields.get( fieldKey ); if( mergeTargetField == null ) { System.out.println( " (+) Field \""+objectKey+"::"+fieldKey+"\" not found in target; adding"); mergeTarget.fFields.put( fieldKey, mergeSourceField ); } else { System.out.println(" Field "+objectKey+"::"+fieldKey+" now has SIFVersion range of " + mergeTargetField.getLatestVersion() + " - " + mergeSourceField.getEarliestVersion() ); mergeTargetField.mergeFrom( mergeSourceField, mergeVersion, objectKey ); } } } private void mergeEnums(DB mergeTargetDB) throws MergeException { System.out.println("- Processing enumerated types..."); for( Enumeration e = fEnums.keys(); e.hasMoreElements(); ) { String key = (String)e.nextElement(); EnumDef val = fEnums.get(key); EnumDef targetEnum = (EnumDef)mergeTargetDB.fEnums.get(key); if( targetEnum == null ) { // Add the missing EnumDef to the target System.out.println( " (+) \""+key+"\" not found in target; adding"); mergeTargetDB.fEnums.put( key, val ); } else { // Visit all values in the target's enumeration and add values // for any that are new in this version of SIF String key2 = null; Hashtable<String,ValueDef> enums = val.fValues; for( Enumeration k = enums.keys(); k.hasMoreElements(); ) { key2 = (String)k.nextElement(); if( targetEnum.fValues.containsKey(key2) ) { ValueDef enumValue = enums.get( key2 ); ValueDef existing = targetEnum.fValues.get( key2 ); if( !(existing.getValue().equals( enumValue.getValue() ) ) ){ throw new MergeException( "Cannot add enum value {" + key2 + "} to " + val.getName() + ". Value {" + enumValue.getValue() + "} does not match value {" + existing.getValue() + "}" ); } if( enumValue.fDesc != null && enumValue.fDesc.length() > 0 ){ // Update the description for this enum value existing.setDesc( enumValue.fDesc ); } } else { System.out.println( " (~) \""+key+"::"+key2+"\" not found in target; adding"); targetEnum.fValues.put( key2, enums.get(key2) ); } } } } // Update the target's SIFVersion range for( Enumeration e = mergeTargetDB.fEnums.keys(); e.hasMoreElements(); ) { String key = (String)e.nextElement(); EnumDef enumDef = (EnumDef)mergeTargetDB.fEnums.get(key); enumDef.setLatestVersion( fVersion ); System.out.println("Enum "+enumDef.getName()+" now has SIFVersion range of "+enumDef.getEarliestVersion()+".."+enumDef.getLatestVersion()); } } public static boolean areEqual( String a, String b ){ if( a == null ){ return b == null; } return a.equals( b ); } public void defineDefinitionFile( DefinitionFile def ) { fDefinitionFiles.put( def.getLocalPackage(), def ); } public DefinitionFile getDefinitionFile( String packageName ) { return fDefinitionFiles.get( packageName ); } public Set<String> getDefinitionFileKeysSet() { return fDefinitionFiles.keySet(); } public ObjectDef defineObject( int id, String name, String pkg ) { ObjectDef d = (ObjectDef)fObjects.get(name); if( d == null ) { d = new ObjectDef(id,name,pkg,fVersion); fObjects.put(name,d); } else { if( !d.fPackage.equals( pkg ) ){ throw new RuntimeException( "Cannot define object " + name + " as belonging to more than one package: " + pkg + "/" + d.fPackage ); } } return d; } public ObjectDef[] getObjects() { int i = 0; ObjectDef[] arr = new ObjectDef[fObjects.size()]; for( Enumeration e = fObjects.elements(); e.hasMoreElements(); i++ ) { arr[i] = (ObjectDef)e.nextElement(); } return arr; } public Collection<ObjectDef> getObjectCollection() { return fObjects.values(); } public ObjectDef getObject( String name ) { if( name == null ){ return null; } if( name.equals( "SIFTime") || name.equals( "Time" ) ) { /* SIFTime objects are treated differently than any other field type */ return fSIFTimeDef; } else { return (ObjectDef)fObjects.get(name); } } public void defineEnum( String name, EnumDef enumDef ) { fEnums.put( name, enumDef); } public EnumDef[] getEnums() { int i = 0; EnumDef[] arr = new EnumDef[fEnums.size()]; for( Enumeration e = fEnums.elements(); e.hasMoreElements(); i++ ) { arr[i] = (EnumDef)e.nextElement(); } return arr; } public EnumDef getEnum( String enumName ) { return fEnums.get( enumName ); } public SIFVersion getVersion() { return fVersion; } public String getNamespace() { return fNamespace; } }