// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.generator; import java.util.*; /** * Encapsulates a DataObject (<object>) or Infrastructure Message (<infra>) definition * * */ public class ObjectDef extends AbstractDef { public static final int FLAG_TOPICOBJECT = 0x00100000, FLAG_EMPTYOBJECT = 0x00200000, FLAG_INFRAOBJECT = 0x00400000, FLAG_OBJECTCOLLAPSED = 0x00800000; protected Hashtable<String, FieldDef> fFields = new Hashtable<String,FieldDef>(); protected int fFieldSeq = 1; protected String fPackage; protected String fSuperclass; protected String fExtrasFile; protected String fRenderAs; protected ADKElementType fElementType; /** * The shared flag indicates this object serves as the superclass for * one or more sub-classes. For example, "CountryOfResidency" uses "Country" * as its superclass, so the "Country" object is marked as shared in the * definition file. */ protected boolean fShared; /** * If this object accepts a text value, holds the datatype of that value. */ private FieldType fValueType; /** * Constructor * @param id Sequence number of this object * @param name Element name (e.g. "StudentPersonal", "OtherId", etc.) * @param localPackage Package name (e.g. "common", "student", "food", etc.) * @param version Version of SIF this object is being defined for */ public ObjectDef( int id, String name, String localPackage, SIFVersion version ) { fName = name; fPackage = localPackage; } /** * Gets the local package name where this object's class should be generated. * The local package name excludes the "openadk.library." prefix */ public String getLocalPackage() { return fPackage; } /** * Gets the name used to represent this object in the DTD class. A static * String is defined with this name, having a value equal to the string * returned by getName. */ public String getDTDSymbol() { return getName().toUpperCase(); } public String getPackageQualifiedDTDSymbol() { return getPackageDTDName() + "." + getDTDSymbol(); } public String getPackageDTDName() { return fPackage.substring( 0, 1 ).toUpperCase() + fPackage.substring( 1 ) + "DTD"; } /** * Returns the ADKElementType of this object, which can be used to determine what the * base class of this SDO object will be. The ADK defines several subclasses of SIFElement * that can be used as the base class for specialized elements within SIF, such as SIFActionList * @return */ public ADKElementType getElementType( DB db, CodeGenerator generator ) { if( this.fName.equals( "MedicalAlertMsg" ) ){ System.out.println("Time to debug" ); } if( fElementType == null ){ if( this.isTopic() ){ fElementType = ADKElementType.SIFDATAOBJECT; } else if( fSuperclass != null && !fSuperclass.equals( "SIFElement" ) ) { if( fSuperclass.startsWith( "SIFActionList" ) ){ fElementType = ADKElementType.SIFACTIONLIST; } else { fElementType = ADKElementType.SIFEELEMENT; } } else { FieldDef childType = this.getRepeatableChildDef(); if( childType != null ){ // ObjectDef childObject = db.getObject( childType.getFieldType().getClassType() ); // if( childObject == null ){ // throw new RuntimeException("Unable to find object definition {" + // childType.getFieldType().getClassType() + "} for member: " + // this.getName() + "." + childType.getName() ); // } // FieldDef[] childKeys = childObject.getKey(); // // // // TODO: The ADK has to glean the difference between an ActionList and // // a normal list from the metadata, so we need to add this information. // // // if( childKeys != null && childKeys.length > 0 ){ // // fElementType = ADKElementType.SIFACTIONLIST; // } else { fElementType = ADKElementType.SIFLIST; // } } else { FieldDef[] childKeys = this.getKey( generator ); if( childKeys != null && childKeys.length == 1 ){ fElementType = ADKElementType.SIFKEYEDELEMENT; } else { fElementType = ADKElementType.SIFEELEMENT; } } } } return fElementType; } /** * Gets the element tag name */ @Override public String getName() { return fName; } public String getExtrasFile() { return fExtrasFile; } public void setExtrasFile( String fn ) { fExtrasFile = fn; } public String getSuperclass() { return fSuperclass; } public String getClassDefinitionString() { if( fFields.size() == 1 ){ FieldDef[] fields = new FieldDef[1]; fFields.values().toArray( fields ); if( fields[0].isRepeatable() ){ return fields[0].getFieldType().getClassType(); } } return null; } /** * If this object is a repeatable elements container, this method returns the * FieldDef of the repeatable child * @return */ public FieldDef getRepeatableChildDef() { if( fFields.size() == 1 ){ FieldDef[] fields = new FieldDef[1]; fFields.values().toArray( fields ); if( fields[0].isRepeatable() ){ return fields[0]; } } return null; } public void setSuperclass( String cls ) { fSuperclass = cls; } public void setRenderAs( String tag ) { fRenderAs = tag; } public String getRenderAs() { return fRenderAs; } public String getTag() { if( fRenderAs == null ){ return fName; } return fRenderAs; } public boolean isEmpty() { return( fFlags & FLAG_EMPTYOBJECT ) != 0; } /** * Is this object a top-level SIF object such as StudentPersonal? */ public boolean isTopic() { return( fFlags & FLAG_TOPICOBJECT ) != 0; } /** * Is this an <infra> object describing a SIF Infrastructure message? */ public boolean isInfra() { return( fFlags & FLAG_INFRAOBJECT ) != 0; } /** * Indicates this is an <infra> object describing a SIF Infrastructure message */ public void setInfra() { fFlags |= FLAG_INFRAOBJECT; } /** * Does this object serve as the superclass for one or more subclasses? */ public boolean isShared() { return fShared; } public void setShared( boolean shared ) { fShared = shared; } /** * Sets the topic flag */ public void setTopic( boolean topic ) { if( topic ) fFlags |= FLAG_TOPICOBJECT; else fFlags &= ~FLAG_TOPICOBJECT; } public FieldDef defineAttr( String name, String classType ) throws ParseException { return defineAttrOrElement(name,classType,FieldDef.FLAG_ATTRIBUTE); } public FieldDef defineElement( String name, String classType ) throws ParseException { return defineAttrOrElement(name,classType,FieldDef.FLAG_ELEMENT); } protected FieldDef defineAttrOrElement( String name, String classType, int nodeType ) throws ParseException { FieldDef d = fFields.get(name); if( d == null ) { d = new FieldDef(this,name,classType, fFieldSeq++, nodeType); fFields.put(name,d); } return d; } public FieldDef getField( String name ) { return fFields.get(name); } public FieldDef[] getAllFields() { List<FieldDef> v = new Vector<FieldDef>(); for( Enumeration<FieldDef> e = fFields.elements(); e.hasMoreElements(); ) { v.add( e.nextElement() ); } Collections.sort( v ); // Sort by sequence # first FieldDef[] returnValue = new FieldDef[ v.size() ]; v.toArray( returnValue ); return returnValue; } public FieldDef[] getDTDFields() { List<FieldDef> v = new Vector<FieldDef>(); for( Enumeration<FieldDef> e = fFields.elements(); e.hasMoreElements(); ) { v.add( e.nextElement() ); } Collections.sort( v ); // Sort by sequence # first FieldDef[] returnValue = new FieldDef[ v.size() ]; v.toArray( returnValue ); return returnValue; } public FieldDef[] getAttributes() { return getFields( FieldDef.FLAG_ATTRIBUTE, null ); } public FieldDef[] getElements() { return getFields( FieldDef.FLAG_ELEMENT, null ); } protected FieldDef[] getFields( int flags, CodeGenerator generator ) { Vector<FieldDef> v = new Vector<FieldDef>(); for( Enumeration e = fFields.elements(); e.hasMoreElements(); ) { FieldDef f = (FieldDef)e.nextElement(); if( ( f.getFlags() & flags ) != 0 ) v.addElement(f); } if( ( flags & FLAG_MANDATORY ) != 0){ FieldDef valueDef = getValueDef( generator ); if( valueDef != null ){ v.add( valueDef ); } } Collections.sort( v ); FieldDef[] arr = new FieldDef[v.size()]; v.copyInto(arr); Arrays.sort(arr); return arr; } /** * Get all attributes and elements that are marked mandatory (M) or * required (R) */ public FieldDef[] getMandatoryFields( CodeGenerator generator ) { return getFields( FLAG_MANDATORY, generator ); } /** * For complex fields, returns the names of the fields that serve as the * object's key. By default this method returns all attributes marked with * an "R" flag (and if no attributes exist or none are marked with an "R" * flag, returns all elements marked with an "R" flag). */ public FieldDef[] getKey( CodeGenerator generator ) { FieldDef[] attrs = getAttributes(); // If this object is a Topic and it has a RefId, return the RefId if( isTopic() ){ for( int i = 0; i < attrs.length; i++ ) { if( attrs[i].getName().equals("RefId") ) { int f = attrs[i].getFlags(); if( ( f & FieldDef.FLAG_NOT_A_KEY ) == 0 ){ return new FieldDef[]{ attrs[i] }; } } } } Vector<FieldDef> v = new Vector<FieldDef>(); int loop = 0; while( attrs != null ) { for( int i = 0; i < attrs.length; i++ ) { int f = attrs[i].getFlags(); if( ( ( f & AbstractDef.FLAG_MANDATORY ) != 0 ) && ( ( f & FieldDef.FLAG_NOT_A_KEY ) == 0 ) ) v.addElement(attrs[i]); } // There are a few inconsistencies in the SIF schema such that some // objects' keys are described by elements, not attributes. So, if we // get here and have no keys, loop again processing element fields // instead of attribute fields. // if( v.size() == 0 && loop == 0 ) attrs = getElements(); else attrs = null; loop++; } // If this is a simple text-only element, return the value def for the element if( v.size() == 0 ){ FieldDef value = this.getValueDef( generator ); if( value != null ) { v.add( value ); } } FieldDef[] arr = new FieldDef[v.size()]; v.copyInto(arr); Arrays.sort(arr); return arr; } /** * If this this object accepts an element value <element>123.123</element>, * the datatype of that value is set in the metadata using a "datatype" element * @param type */ public void setDataType( String dataType ){ fValueType = FieldType.getFieldType( dataType ); } public void setEnumType( String enumType ) throws ParseException{ fValueType = FieldType.toEnumType( FieldType.getFieldType( "String" ), enumType ); } /** * Does this element have a text value? * @return true if this element has no elements or attributes, or it has * no elements but at least one attribute and the FLAG_EMPTYOBJECT flag * * is not set */ public FieldType getValueType() { if( fValueType == null ){ if( ( fFlags & FLAG_EMPTYOBJECT ) == 0 ){ if( fFields == null || fFields.size() == 0 || getElements().length == 0 ){ fValueType = FieldType.getFieldType( "String" ); } } } return fValueType; } /** * * @return */ public FieldDef getValueDef( CodeGenerator generator ){ FieldDef returnValue = null; FieldType valueType = getValueType(); if( valueType != null ){ try{ returnValue = new FieldDef( this, "Value", valueType, 999, FieldDef.FLAG_TEXT_VALUE | AbstractDef.FLAG_MANDATORY ); } catch (ParseException parseEx ){ System.out.println( parseEx ); throw new RuntimeException( parseEx.toString(), parseEx ); } returnValue.setDesc( "Gets or sets the content value of the <" + this.fName + "> element" ); returnValue.setEarliestVersion( this.getEarliestVersion() ); returnValue.setLatestVersion( this.getLatestVersion() ); returnValue.setElementDefConst( this.getPackageQualifiedDTDSymbol() ); } return returnValue; } public void addAlias(SIFVersion version, String renderAs) { if( fAliases == null ){ fAliases = new HashMap<String, List<SIFVersion>>(); } List<SIFVersion> versions = fAliases.get( renderAs ); if( versions == null ){ versions = new ArrayList<SIFVersion>(); } versions.add( version ); fAliases.put( renderAs, versions ); } /** * Returns a map of aliases this Object has in other versions, along * with a list of SIFVersions it applies to * @return */ public Map<String, List<SIFVersion>> getAliases() { return fAliases; } private Map<String, List<SIFVersion>> fAliases = null; public String getPackage() { return fPackage; } }