// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.generator; import java.util.*; public class FieldDef extends AbstractDef implements Comparable { public static final int FLAG_DO_NOT_ENCODE = 0x00001000, FLAG_ELEMENT = 0x10000000, FLAG_ATTRIBUTE = 0x20000000, FLAG_COMPLEX = 0x40000000, FLAG_NOT_A_KEY = 0x80000000, FLAG_TEXT_VALUE = 0x01000000, FLAG_COLLAPSED = 0x02000000; protected FieldType fFieldType; protected String fSuperclass; protected int fSequence; protected ObjectDef fParent; protected String fRenderAs; protected String fDtdSymbol; /** * If this field is known by more than one tag, or has different sequence * numbers for different versions of SIF, an Alias is added to the table * by the DB.mergeInto method. Alias entries are keyed by SIF version string. */ protected SortedSet<Alias> fAliases; private String fElementDefConst; private String fSurrogate; public FieldDef( ObjectDef parent, String name, String classType, int sequence, int nodeTypeFlag ) throws ParseException { this( parent, name,FieldType.getFieldType( classType ), sequence, nodeTypeFlag ); } public FieldDef( ObjectDef parent, String name, FieldType fieldType, int sequence, int nodeTypeFlag ) throws ParseException { fParent=parent; fSequence=sequence; fName=name; fFieldType= fieldType; fFlags = nodeTypeFlag; if( fFieldType.isComplex() ) { if( fFlags == FieldDef.FLAG_ATTRIBUTE ){ throw new ParseException( "Cannot create an attribute as a ComplexType: " + parent.getName() + "." + name + " {" + fieldType.getClassType() + "}"); } fFlags |= FLAG_COMPLEX; if( fieldType.getClassType().equals(name) ) fSuperclass = fieldType.getClassType(); } fDtdSymbol = fParent.getDTDSymbol()+"_"+(getName().toUpperCase()); fElementDefConst = fParent.getPackageQualifiedDTDSymbol() +"_"+(getName().toUpperCase()); } /** * Allows the def to validate itself after all values are set * @return */ @Override public void validate() throws ParseException { if( isRepeatable() && !fFieldType.isComplex() ){ throw new ParseException( "Cannot create a repeatable element that is not a complex type: " + fParent.getName() + "." + fName ); } } /** * Returns true if this field is repeatable. If the element switches repeatability * between SIF Version 1.5r1 and 2.0, the repeatability value from 2.0 is returned, * since all ADK apis are derived from their 2.0 version * @return True if this element is repeatable in all versions or in version SIF 2.0 * or greater */ public boolean isRepeatable() { int flags = fFlags; if( fAliases != null ){ // Search for the flags from SIF 2.0 Alias latestAlias = fAliases.last(); if( latestAlias.getVersion().getMajor() == 2 ){ flags = latestAlias.getFlags(); } } return ( flags & AbstractDef.FLAG_REPEATABLE ) > 0; } /** * Prints a textual description of the specified flags value for debugging purposes * @param flags * @return */ public static String flagsToString( int flags ) { StringBuilder sb = new StringBuilder(); if( ( flags & FLAG_ELEMENT ) > 0 ){ sb.append( "ELEMENT | " ); } if( ( flags & FLAG_ATTRIBUTE ) > 0 ){ sb.append( "ATTRIBUTE |" ); } if( ( flags & FLAG_COMPLEX ) > 0 ){ sb.append( "COMPLEX |" ); } if( ( flags & FLAG_NOT_A_KEY ) > 0 ){ sb.append( "NOT_A_KEY |" ); } if( ( flags & FLAG_TEXT_VALUE ) > 0 ){ sb.append( "TEXT_VALUE |" ); } if( ( flags & FLAG_COLLAPSED ) > 0 ){ sb.append( "COLLAPSED |" ); } if( ( flags & FLAG_OPTIONAL ) > 0 ){ sb.append( "OPTIONAL |" ); } if( ( flags & FLAG_MANDATORY ) > 0 ){ sb.append( "MANDATORY |" ); } if( ( flags & FLAG_CONDITIONAL ) > 0 ){ sb.append( "CONDITIONAL |" ); } if( ( flags & FLAG_REPEATABLE ) > 0 ){ sb.append( "REPEATABLE |" ); } return sb.toString(); } /** * Adds a new Alias to this field * @param version * @param tag * @param surrogate * @param sequence * @param flags * @throws MergeException */ private void addAlias( SIFVersion version, String tag, String surrogate, int sequence, int flags ) throws MergeException { if( version.compareTo( this.getEarliestVersion() ) < 1 ){ throw new MergeException( "Cannot add an alias. Version precedes earliest version" ); } if( fAliases == null ){ fAliases = new TreeSet<Alias>(); } else if( version.compareTo( fAliases.last().getVersion() ) < 1 ) { // Ensure that the new version being added as an alias is greater than // the latest alias that was defined throw new MergeException( getTag() + " alias already defined for " + version ); } Alias a = new Alias( version, tag, surrogate, sequence ); a.setFlags( flags ); fAliases.add( a ); } /** * Gets a list of all Aliases defined for this field, sorted by SIF Version * @return a list of all Aliases defined for this field, sorted by SIF Version */ public SortedSet<Alias> getAliases() { return fAliases; } /** * Returns an array of Aliases that have unique tag names * @return an array of Aliases that have unique tag names */ public Alias[] getUniqueTagAliases(){ Alias last = new Alias( getEarliestVersion(), getTag(), getSurrogate(), getSequence() ); if( fAliases == null ){ return new Alias[] { last }; } ArrayList<Alias> allAliases = new ArrayList<Alias>(); allAliases.add( last ); for( Alias alias : fAliases ){ if( !last.getTag().equals( alias.getTag() ) ){ last = alias; allAliases.add( alias ); } } Alias[] returnValue = new Alias[ allAliases.size()]; allAliases.toArray( returnValue ); return returnValue; } public int compareTo( Object o ) { int cmp = ((FieldDef)o).fSequence; if( fSequence < cmp ) return -1; if( fSequence > cmp ) return 1; return 0; } /** * Gets the name used to represent this object in the DTD class. * * A static String is defined in the DTD class having the name "parent_this", * where "parent" is the value returned by the parent ObjectDef's getDTDSymbol * and "this" is the name of this field in uppercase (e.g. "STUDENTPERSONAL_REFID"). * The value of that static will be the string returned by getName. */ public String getDTDSymbol() { return fDtdSymbol; } /** * Gets the ElementDef const to use for this field. * @param generator The generator to use to generate the ElementDef const name * if it is an element value * @return */ public String getElementDefConst( CodeGenerator generator ) { if( fElementDefConst == null ){ return null; } else { return fElementDefConst; } } public void setElementDefConst(String value) { fElementDefConst = value; } public void setDTDSymbol( String symbol ) { fDtdSymbol = symbol; } @Override public String getName() { return fName; } public int getSequence() { if( fSeqOverride != -1 ) return fSeqOverride; return fSequence; } public void setEnum( String enumName ) throws ParseException { fFieldType = FieldType.toEnumType( fFieldType, enumName ); } public String getSuperclass() { if( fSuperclass == null ) return fName; return fSuperclass; } public FieldType getFieldType() { return fFieldType; } public void setRenderAs( String tag ) { fRenderAs = tag; } public String getRenderAs() { return fRenderAs; } public String getElementDefExpression() { if( fSurrogate != null ) { return "~" + fSurrogate + ( fRenderAs == null ? "" : fRenderAs ); } return fRenderAs; } public String getTag() { if( fRenderAs == null ) return fName; return fRenderAs; } public boolean isComplex() { return ( fFlags & FLAG_COMPLEX ) != 0; } /** * 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. For nearly all SIF objects this method returns a single * key named "RefId". */ public FieldDef[] getKey() { Vector<FieldDef> v = new Vector<FieldDef>(); FieldDef[] attrs = fParent.getAttributes(); for( int i = 0; i < attrs.length; i++ ) { if( (( attrs[i].getFlags() & FLAG_MANDATORY ) != 0 ) && (( attrs[i].getFlags() & FLAG_NOT_A_KEY ) == 0 ) ) v.addElement(attrs[i]); } FieldDef[] arr = new FieldDef[v.size()]; v.copyInto(arr); return arr; } public void setSurrogate(String attr) { if( attr != null ){ System.out.print( "Surrogate: " + attr ); } fSurrogate = attr; } public String getSurrogate() { return fSurrogate; } public ObjectDef getParent() { return fParent; } /** * Merges the information in the specified field into this field's data set. * @param mergeSourceField The field to merge with this one. This field must come from a DB that represents a newer version of SIF * @param mergeSourceVersion The version of SIF that this merge field represents * @param objectKey The name of the object (used only for logging purposes) * @throws MergeException If the version represents a version that already been merged */ public void mergeFrom(FieldDef mergeSourceField, SIFVersion mergeSourceVersion, String objectKey ) throws MergeException { Alias currentAlias; if( fAliases != null ){ // We have already defined aliases for this field. Therefore, the latest // Alias in fAliases is what we should compare to currentAlias = fAliases.last(); } else { // No aliases have yet been defined for this field. Create a new, fake one // that represents this field's originial values currentAlias = new Alias( getEarliestVersion(), this.getTag(), this.getSurrogate(), this.getSequence() ); currentAlias.setFlags( fFlags ); } if( mergeSourceVersion.compareTo( currentAlias.getVersion() ) < 1 ){ throw new MergeException( "Cannot merge field. Version precedes the latest version already marged. Fields must be merged in order." ); } boolean methodDiff = false; boolean attrDiff = false; // Check the field for differences... if( ( mergeSourceField.fFieldType == null && this.fFieldType != null ) || !( mergeSourceField.fFieldType.equals( this.fFieldType ) ) ) { System.out.println( " (~) Field \""+objectKey+"::"+ this.getName() +"\" has different ClassType; adding version-specific field. 1:" + mergeSourceField.fFieldType + " - 2:" + this.fFieldType ); methodDiff = true; } if( !currentAlias.getTag().equals( mergeSourceField.getTag() ) ) { System.out.println( " (~) Field \""+objectKey+"::"+ this.getName() +"\" has different tag; adding alias"); System.out.println( " "+currentAlias.getVersion()+" -> " + currentAlias.getTag() ); System.out.println( " "+mergeSourceVersion+" -> " + mergeSourceField.getTag()); attrDiff = true; } if( currentAlias.getSequence() != mergeSourceField.getSequence() ) { System.out.println( " (~) Field \""+objectKey+"::"+ this.getName() +"\" has different sequence number; adding alias"); System.out.println( " "+currentAlias.getVersion()+" -> " +currentAlias.getSequence() ); System.out.println( " "+mergeSourceVersion+" -> " + mergeSourceField.getSequence() ); attrDiff = true; } if( currentAlias.getFlags() != mergeSourceField.getFlags() ) { System.out.println( " (~) Field \""+objectKey+"::"+ this.getName() +"\" has different flags adding alias"); System.out.println( " "+currentAlias.getVersion()+" -> " + FieldDef.flagsToString( currentAlias.getFlags() ) ); System.out.println( " "+mergeSourceVersion+" -> " + FieldDef.flagsToString( mergeSourceField.getFlags() ) ); attrDiff = true; } String existingSurrogate = currentAlias.getSurrogate(); String srcSurrogate = mergeSourceField.getSurrogate(); if( !DB.areEqual( existingSurrogate, srcSurrogate ) ) { System.out.println( " (~) Field \""+objectKey+"::"+this.getName()+"\" has different surrogate adding alias"); System.out.println( " "+currentAlias.getVersion()+" -> " +currentAlias.getSurrogate() ); System.out.println( " "+mergeSourceVersion+" -> " + srcSurrogate); attrDiff = true; // Validation of metadata. Surrogates should only appear in 1.1 and 1.5r1 if( existingSurrogate != null && srcSurrogate != null ){ // Warning throw new RuntimeException( "WARNING: TWO DIFFERENT SURROGATE VALUES FOUND, POSSIBLE METADATA BUG!" ) ; } if( existingSurrogate == null ){ // Warning throw new RuntimeException( "WARNING: Switching from null to Surrogate, POSSIBLE METADATA BUG!" ); } if( mergeSourceVersion.equals( SIFVersion.SIF15r1 ) ){ // Warning throw new RuntimeException( "WARNING: Surrogate was added or changed in 1.5r1, POSSIBLE METADATA BUG!" ); } } if( methodDiff ) { // If there were any differences that would result in new // methods to the implementation class, create a new FieldDef System.out.println("*** DIFF ***"); throw new MergeException( "Method merge not yet supported" ); } else if( attrDiff ) { // If there were any differences in tag name or sequence number, add an alias to the FieldDef this.addAlias( mergeSourceVersion, mergeSourceField.getTag(), mergeSourceField.getSurrogate(), mergeSourceField.getSequence(), mergeSourceField.getFlags() ); } this.setLatestVersion( mergeSourceVersion ); // Update the field description to the latest if this version has documentation if( mergeSourceField.fDesc != null && mergeSourceField.fDesc.length() > 0 ){ this.setDesc( mergeSourceField.fDesc ); } } }