Package org.dmd.dmc

Source Code of org.dmd.dmc.DmcObject

//  ---------------------------------------------------------------------------
//  dark-matter-data
//  Copyright (c) 2010 dark-matter-data committers
//  ---------------------------------------------------------------------------
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU Lesser General Public License as published by the
//  Free Software Foundation; either version 3 of the License, or (at your
//  option) any later version.
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
//  more details.
//  You should have received a copy of the GNU Lesser General Public License along
//  with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
//  ---------------------------------------------------------------------------
package org.dmd.dmc;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;

import org.dmd.dmc.types.DmcTypeComplexTypeWithRefs;
import org.dmd.dmc.types.DmcTypeModifier;
import org.dmd.dmc.types.DmcTypeNamedObjectREF;
import org.dmd.dmc.types.Modifier;
import org.dmd.dmc.types.StringName;
import org.dmd.dms.generated.dmo.ClassDefinitionDMO;
import org.dmd.dms.generated.enums.ClassTypeEnum;
import org.dmd.dms.generated.enums.DataTypeEnum;
import org.dmd.dms.generated.enums.ModifyTypeEnum;
import org.dmd.dms.generated.enums.ValueTypeEnum;
import org.dmd.dms.generated.types.ClassDefinitionREF;
import org.dmd.dms.generated.types.DmcTypeClassDefinitionREFMV;
import org.dmd.dms.generated.types.DmcTypeModifierMV;

/**
* The Dark Matter Core (DMC) Object is the basic entity on which all aspects of the
* Dark Matter Data framework are based.
* <P>
* The DMC object is basically a typed holder for a collection of attributes. At this
* level, the DMC is abstract; it is the derived, auto-generated, Dark Matter Objects (DMOs)
* that give a DMC identity. Storing the attributes as a collection introduces a small
* amount of overhead to DMOs, but allows for operational concepts such as serialization,
* modification, formatting, cloning, staging etc. that are all provided automatically.
* It also allows for the use of DMOs in environments where accessing values by name
* (for example in GWT-based applications) is a necessity.
* <p>
* In addition to the attribute collection, the only constant overhead for a DMC object
* is the info Vector. The info Vector will contain various information at various times
* during the life cycle of an object, but will only take up additional space as required.
* Again, there is some overhead, but the benefits of that overhead are very useful.
* <p>
* There are currently 6 additional pieces of information stored for a DMC object:
* <ul>
* <li>
* CONTAINER[0] - if the DMO is wrapped by a DMW generated class, this holds the
* reference to the DMW. This supports the automatic casting of a DMO to its DMW wrapper
* type via the use of auto generated Iterable classes.
* </li>
* <li>
* BACKREFS[1] - in order to behave nicely with Java garbage collection, the named
* object reference mechanisms of Dark Matter will maintain knowledge of which resolved
* reference attributes are pointing at a particular DMO instance. If the DMO is deleted,
* it can automatically remove the reference to itself in the reference attributes. This is
* a work around for the fact that the GWT JRE Emulation mechanisms don't support the
* java.lang.ref.WeakReference mechanisms. Back reference tracking must be enabled
* via the DmcOmni.instance.backRefTracking() method.
* </li>
* <li>
* MODIFIER[2] - the Modifier is used to record changes that are made to an object so that
* the modifications can be distributed via the Dark Matter Protocol Event (DMPEvent) and
* then applied to remote objects. This mechanism supports very succinct notification of
* changes to objects and only includes the object deltas, not the entire object. The MODIFIER
* and the LASTVAL (described next) will only exist on the object if it has been modified.
* </li>
* <li>
* LASTVAL[3] - the LASTVAL is just a handle to the last value that was added to or deleted
* from a multi-valued attribute. It is required to support the Modifier concept.
* </li>
* <li>
* MODREC[4] - the MODREC flag is just a Boolean to indicate that the object is a
* temporary construct that will be used as a MODification RECorder to populate a
* DmcTypeModifier with a set of changes to be applied to an object in a cache or a
* remote object. This value is set when a DMO/DMW is constructed with a DmcTypeModifierMV.
* This distinction is required to prevent applying backrefs to an object when an object
* referring to it is in MODREC mode. If we didn't keep track of this, we would wind up
* tracking references twice.
* </li>
* <li>
* STAGING[5] - the STAGING flag is used to indicate that the current object is a staging
* area for the evaluation of a set of modifications that are about to be applied to
* an existing object. This mechanism exists to facilitate the application of validation
* rules to the results of a set of modifications. Instead of cloning an entire object,
* which could be costly, a staging object will only clone the attributes of a
* target object that are referred to in the modifications; the other attributes of
* the staging object are merely references to those in the target object. The staging
* object can then be passed to the rule manager for validation and, if the resulting
* object is valid, the modifier can be applied to the target object. While in STAGING
* mode, the object reference tracking mechanisms are disabled so that we don't track
* changes that could, in the end not be valid (and also, we don't want to apply them twice).
* </li>
* </ul>
*/
@SuppressWarnings("serial")
abstract public class DmcObject implements Serializable {
 
  // The indices of data stored in our dynamic info vector
  static final int  CONTAINER     = 0;
  static final int  BACKREFS    = 1;
  static final int  MODIFIER    = 2;
  static final int  LASTVAL      = 3;
  static final int  MODREC      = 4;
  static final int  STAGING      = 5;
 
  // The associated sizes of the info vector when storing various information.
  // All of this information is static to cut down on needless operations.
  static final int  CONTAINER_SIZE   = 1;
  static final int  BACKREFS_SIZE  = 2;
  static final int  MODIFIER_SIZE  = 3;
  static final int  LASTVAL_SIZE  = 4;
  static final int  MODREC_SIZE    = 5;
  static final int  STAGING_SIZE  = 6;
 
  // The objectClass attribute is common to all objects and indicates the construction class
  // and any auxiliary classes associated with the object
    public final static DmcAttributeInfo __objectClass = new DmcAttributeInfo("objectClass",1,"ClassDefinitionREF",ValueTypeEnum.MULTI,DataTypeEnum.PERSISTENT);
 
  // At this level, all we have is a simple collection of attributes.
  protected Map<Integer, DmcAttribute<?>>  attributes;
 
  // The info map is used to reduce the memory footprint of the DmcObject by compacting
  // various additional information that may be required into a single value. This comes
  // with a slight processing overhead, but that's seen to be reasonable when you're
  // trying to store lots of DMOs in memory.
  //
  transient Vector<Object>        info;
 
  public DmcObject(){
    attributes   = new HashMap<Integer, DmcAttribute<?>>();
    info    = null;
  }
 
  /**
   * A protected constructor for derived classes that lets us set the object class
   * attribute from the most specific derived class.
   * @param oc The class name.
   */
  protected DmcObject(String oc){
    attributes   = new HashMap<Integer, DmcAttribute<?>>();
    info    = null;
   
    // All objects have a well known construction class which is the first
    // entry in their objectClass attribute.
        DmcAttribute<?> attr = new DmcTypeClassDefinitionREFMV(__objectClass);
        try {
            attr.add(new StringName(oc));
      add(__objectClass,attr);
    } catch (DmcValueException e) {
      throw(new IllegalStateException("Setting the objectClass using a String shouldn't ever croak!", e));
    }
  }
 
  /**
   * @return the number of attributes excluding the objectClass.
   */
  public int numberOfAttributes(){
    return(attributes.size() - 1);
  }
 
  /**
   * @return The class definition reference for this object. Realize that this may just contain the
   * name of the class, not the reference to the ClassDefinition itself; it all depends on the
   * operational context.
   */
  public Iterator<ClassDefinitionREF> getObjectClass(){
    DmcTypeClassDefinitionREFMV objClass = (DmcTypeClassDefinitionREFMV) attributes.get(__objectClass.id);
    if (objClass == null)
      throw(new IllegalStateException("A DMO shouldn't exist without its objClass attribute!"));
    return(objClass.getMV());
  }
 
  /**
   * @return The class definition reference for this object. Realize that this may just contain the
   * name of the class, not the reference to the ClassDefinition itself; it all depends on the
   * operational context.
   */
  public ClassDefinitionREF getConstructionClass(){
    DmcTypeClassDefinitionREFMV objClass = (DmcTypeClassDefinitionREFMV) attributes.get(__objectClass.id);
    if (objClass == null)
      throw(new IllegalStateException("A DMO shouldn't exist without its objClass attribute!"));
    return(objClass.getMVnth(0));
  }
 
  /**
   * @return The class info associated with this object.
   */
  public DmcClassInfo getConstructionClassInfo(){
    return(getConstructionClass().getClassInfo());
  }
 
  /**
   * @return an iterator over this object's attribute values.
   */
  public Iterator<DmcAttribute<?>> getAttributeIterator(){
    return(attributes.values().iterator());
  }
 
  /**
   * If a class of object doesn't support back reference tracking, it will overload this
   * method to return false. Otherwise, back reference tracking can be turned on for all
   * objects. This distinction is important for things like Dark Matter Protocol messages
   * where we would want to ensure that object references could be resolved, but we wouldn't
   * want to update back references in those objects to which the messages referred.
   * @return true if the object supports backref tracking.
   */
  public boolean supportsBackrefTracking(){
    return(true);
  }
 
  /**
   * We cycle through the DmcClassInfo associated with our object class and any auxiliary classes
   * and determine if the attribute is allowable.
   * @param ai The attribute info for the attribute in question.
   * @return true is the attribute is allowed and false otherwise.
   */
  public boolean allowsAttribute(DmcAttributeInfo ai){
    boolean rc = false;
   
    if (ai.id == __objectClass.id)
      return(true);
   
    DmcTypeClassDefinitionREFMV objClass = (DmcTypeClassDefinitionREFMV) attributes.get(__objectClass.id);
    for(int i=0; i<objClass.getMVSize(); i++){
      if (objClass.getMVnth(i).getClassInfo().allowsAttribute(ai)){
        rc = true;
        break;
      }
    }
   
    return(rc);
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Abstracts that are overridden in DMOs

  /**
   * Auto-generated derived classes override this to return an empty instance
   * of themselves. This supports cloning.
   * @return An instance of the derived class.
   */
  abstract public DmcObject getNew();
 
  /**
   * Auto-generated derived classes override this to return an instance of themselves
   * with just the specified slice of attributes. In the case of named objects, the
   * object name is always part of the slice.
   * @return An instance of the derived class with the specified slice of attributes.
   */
  abstract public DmcObject getSlice(DmcSliceInfo info);
 
  protected Map<Integer,HashMap<String,DmcAttributeValidator>> getAttributeValidators(){
    throw(new IllegalStateException("getAttributeValidators() must be overriden in the DMO"));
  }
 
  protected Map<String,DmcObjectValidator> getObjectValidators(){
    throw(new IllegalStateException("getObjectValidators() must be overriden in the DMO"));
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Utility mechanisms to manage the info Vector
 
  /**
   * Sets the container that's currently wrapping this object.
   * @param c the container.
   */
  public void setContainer(DmcContainerIF c){
    if (isStaging())
      throw(new IllegalStateException("You can't call setContainer() on a STAGING object."));
    setInfo(CONTAINER,CONTAINER_SIZE,c);
  }
 
  /**
   * @return The container that's currently wrapping this object or null if there isn't one.
   */
  public DmcContainerIF getContainer(){
    return (DmcContainerIF) (getInfo(CONTAINER,CONTAINER_SIZE));
  }
 
  /**
   * This method is used when we're tracking backrefs. The modifier will contain the
   * OPPOSITE of the SET/ADD operation that's creating a reference to THIS object.
   * @param mod
   */
  public void addBackref(Modifier mod){
    if (isStaging())
      throw(new IllegalStateException("You can't call addBackref() on a STAGING object."));
   
    synchronized (attributes){   
      if (getBackref() == null){
        setInfo(BACKREFS,BACKREFS_SIZE,new DmcTypeModifierMV());
      }
      try {
        getBackref().add(mod);
      } catch (DmcValueException e) {
        throw(new IllegalStateException("Backref modifier shouldn't throw exceptions.",e));
      }
    }
  }
 
  /**
   * @return true if this object has any other objects referring to it. This mechanism is
   * only valid if you've turned on back reference tracking via DmcOmni.
   */
  public boolean isReferenced(){
    synchronized (attributes){   
      if (getBackref() == null)
        return(false);
      if (getBackref().getMVSize() > 0)
        return(true);
      return(false);
    }
  }
 
  /**
   * @return the number of references to this object.  This mechanism is
   * only valid if you've turned on back reference tracking via DmcOmni.
   */
  public int referenceCount(){
    synchronized (attributes){   
      if (getBackref() == null)
        return(0);
     
      return(getBackref().getMVSize());
    }
  }
 
  /**
   * This method is used to remove back references from an object to the objects
   * that refer to it. Back reference tracking is controlled via the DmcOmni. You
   * shouldn't be calling this directly - if you do, all bets are off!
   * @param mod The modifier representing the removal operation for the back reference.
   */
  public void removeBackref(Modifier mod){
    if (isStaging())
      throw(new IllegalStateException("You can't call removeBackref() on a STAGING object."));
   
    synchronized (attributes){   
      if (getBackref() == null)
        throw(new IllegalStateException("Tried to remove backreference from an object with no backrefs."));
      getBackref().del(mod);
    }
  }
 
  /**
   * Returns the set of modifiers that indicate the back references to this object from other objects.
   * This is only valid if you've turned on back reference tracking via the DmcOmni. DON'T ALTER
   * THE RETURNED VALUE - YOU'VE BEEN WARNED.
   * @return the set of modifiers that will remove the references.
   */
  public DmcTypeModifierMV getBackref(){
    return (DmcTypeModifierMV) (getInfo(BACKREFS, BACKREFS_SIZE));
  }
 
  /**
   * This method sets the modifier of the object which will track all changes made
   * to the object. Set the modifier to null to stop change tracking.
   * @param m
   */
  public void setModifier(DmcTypeModifierMV m){
    if (isStaging())
      throw(new IllegalStateException("You can't call setModifier() on a STAGING object."));
   
    if (m == null)
      shrinkInfo(MODIFIER);
    else
      setInfo(MODIFIER,MODIFIER_SIZE,m);
  }
 
  /**
   * @return the current set of modifications (if any were made).
   */
  public DmcTypeModifierMV getModifier(){
    return (DmcTypeModifierMV) getInfo(MODIFIER, MODIFIER_SIZE);
  }
 
  /**
   * This method is called in the add()/del() methods on DMOs to store the last value that was
   * added/deleted from a multi-valued attribute.
   * @param val
   */
  protected void  setLastValue(Object val){
    // If we don't have a modifier and backref tracking IS NOT turned on, don't bother
    // storing the last value.
    if ( (getModifier() == null) && (!DmcOmni.instance().backRefTracking()) )
      return;
     
    setInfo(LASTVAL,LASTVAL_SIZE,val);
  }

  /**
   * This method is used within DmcObject when adding modify operations for add()/del().
   * @return The last value added or deleted.
   */
  protected Object getLastValue(){
    return(getInfo(LASTVAL,LASTVAL_SIZE));
  }
 
  /**
   * When a DMO is constructed with A DmcTypeModifier, this flag is set to true.
   * @param f
   */
  protected void modrec(Boolean f){
    setInfo(MODREC,MODREC_SIZE,f);
  }
 
  protected Boolean isModrec(){
    Boolean mr = (Boolean) getInfo(MODREC,MODREC_SIZE);
    if (mr == null)
      return(false);
    return(mr);
  }
 
  /**
   * When a DMO is constructed in staging mode, this flag is set to true.
   * @param f
   */
  protected void staging(Boolean f){
    setInfo(STAGING,STAGING_SIZE,f);
  }
 
  protected Boolean isStaging(){
    Boolean staging = (Boolean) getInfo(STAGING,STAGING_SIZE);
    if (staging == null)
      return(false);
    return(staging);
  }
 
  /**
   * This method manages the info vector and grows it to the appropriate size to manage
   * the additional information required.
   * @param index        The index of the information we're storing.
   * @param requiredSize The required size of the info vector when storing this information.
   * @param value        The value to be stored.
   */
  void setInfo(int index, int requiredSize, Object value){
//    DebugInfo.debug("SET INFO " + ((DmcNamedObjectIF)this).getObjectName() + " " + index + " " + System.identityHashCode(this));
    if (info == null){
      // When we first create the info vector, we'll set it to size 1 and
      // then add the number of required spots - so, if we're storing a
      // LASTVAL, we wind up adding 4 empty spots.
      info = new Vector<Object>(1,1);
     
//      DebugInfo.debug("NEW INFO " + ((DmcNamedObjectIF)this).getObjectName() + " " + index + " " + System.identityHashCode(this));
     
      switch(index){
      case MODREC:
        info.add(null);
      case LASTVAL:
        info.add(null);
      case MODIFIER:
        info.add(null);
      case BACKREFS:
        info.add(null);
      case CONTAINER:
        info.add(null);
      case STAGING:
        info.add(null);
      }
    }
    if (info.size() < requiredSize){
      // If info is less than the required size, grow it
      while(info.size() < requiredSize)
        info.add(null);
    }
    info.set(index, value);
  }
 
  /**
   * This method will return the info at the specified index if it's available.
   * @param index
   * @param requiredSize
   * @return
   */
  Object getInfo(int index, int requiredSize){
//    DebugInfo.debug("GET INFO " + ((DmcNamedObjectIF)this).getObjectName() + " " + index + " " + System.identityHashCode(this));
   
    if (info == null){
//      DebugInfo.debug("GET INFO [0] " + ((DmcNamedObjectIF)this).getObjectName() + " " + index + " " + System.identityHashCode(this));
      return(null);
    }
//    DebugInfo.debug("GET INFO [" + info.size() + "] " + ((DmcNamedObjectIF)this).getObjectName() + " " + index + " " + System.identityHashCode(this));
    if (info.size() < requiredSize)
      return(null);
    return(info.get(index));
  }
 
  /**
   * The intention is to keep the size of the info vector as small as possible in order
   * to reduce overhead at the object level. This method is called in instances where
   * info data is no longer required i.e. when the modifier is removed from the object
   * and when there are no longer any backrefs.
   * <p/>
   * We don't bother with the case where we we're in STAGING mode because such objects
   * aren't compatible with other modes.
   * @param index
   */
  void shrinkInfo(int index){
    Vector<Object> newinfo = null;
    if (index == MODIFIER){
      if (info.get(BACKREFS) == null){
        if (info.get(CONTAINER) != null){
          // We just have the container, shrink to 1
          newinfo = new Vector<Object>(1,1);
          newinfo.add(info.get(CONTAINER));
        }
        // If the container was null, we'll wind up nulling the info vector
      }
      else{
        // We have backrefs, so shrink to 2
        newinfo = new Vector<Object>(2,1);
        newinfo.add(info.get(CONTAINER));
        newinfo.add(info.get(BACKREFS));
      }
    }
    else if (index == BACKREFS){
      if (info.size() > BACKREFS_SIZE){
        // For some reason, we have mods and lastvalue, so just leave things
        // When the modifier is cleaned up, we'll also get rid of the backref space
        info.set(BACKREFS, null);
      }
      else{
        if (info.get(CONTAINER) != null){
          // Shrink to 1
          newinfo = new Vector<Object>(1,1);
          newinfo.add(info.get(CONTAINER));
        }
        // If the container was null, we'll wind up nulling the info vector
      }
    }

    info = newinfo;
    newinfo = null;
  }
 

  ////////////////////////////////////////////////////////////////////////////////
  // Back reference tracking

  /**
   * This method should be called by whatever mechanism you're using to manage a collection
   * of DMOs. It will automatically removed references to this object that are contained in
   * object reference attributes if you have set DmcOmni.backRefTracking(true).
   */
  public void youAreDeleted(){
    if (isStaging())
      throw(new IllegalStateException("You can't call youAreDeleted() on a STAGING object."));
   
    if (DmcOmni.instance().backRefTracking()){
      DmcTypeModifierMV mods = getBackref();
      if (mods != null)
        DmcOmni.instance().removeReferences(mods);
     
      // And in the other direction - we see if we have any references to other
      // object and remove the backrefs
      for(DmcAttribute<?> attr : attributes.values()){
        if (attr.ID == __objectClass.id)
          continue;
         
        if (attr instanceof DmcTypeNamedObjectREF<?, ?>){
          ((DmcTypeNamedObjectREF<?, ?>)attr).removeBackReferences();
        }
      }
     
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Attribute information
 
  /**
   * This method is generally used by object parsers to determine whether or not an attribute
   * is natively supported by a DMO (in which case it returns the attribute info) or whether
   * the attribute in question is associated with an auxiliary class (in which case we get
   * the appropriate attribute info from the AttributeDefinition).
   * @param an The attribute id.
   * @return The attribute info or null.
   */
  public DmcAttributeInfo getAttributeInfo(String an){
//    DmcAttributeInfo rc = getStringToAttrInfo().get(an);
    DmcAttributeInfo rc = DmcOmni.instance().getAttributeInfo(an);
   
    if (rc == null){
      if (an.equals(__objectClass.name))
        rc = __objectClass;
    }
   
    return(rc);
  }
 
  /**
   * This method is generally used by object parsers to determine whether or not an attribute
   * is allowed by any of the classes in our objectClass attribute.
   * @param id The attribute id.
   * @return The attribute info or null.
   */
  public DmcAttributeInfo getAttributeInfo(Integer id){
    DmcAttributeInfo rc = null;
//   
//    if (getIdTo AttrInfo() != null)
//      return(getIdToAttrInfo().get(id));
//   
//    return(rc);
//   
       
    if (id == __objectClass.id)
      return(__objectClass);
   
    DmcTypeClassDefinitionREFMV objClass = (DmcTypeClassDefinitionREFMV) attributes.get(__objectClass.id);
    for(int i=0; i<objClass.getMVSize(); i++){
      rc = objClass.getMVnth(i).getClassInfo().allowsAttribute(id);
      if (rc != null)
        break;
    }
   
    return(rc);

  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Object class access/manipulation
 
  /**
   * @return The class name of this object.
   */
  public String getConstructionClassName(){
    DmcTypeClassDefinitionREFMV ocl = (DmcTypeClassDefinitionREFMV) get(__objectClass.id);
   
    if (ocl != null){
      // HACK - attrInfo
      ocl.getAttributeInfo();
      if (ocl.getMVSize() > 0){
        return(ocl.getMVnth(0).getObjectName().getNameString());
      }
    }
   
    return(null);
  }
 
  /**
   * @return The full class name of this object with auxiliary classes included
   */
  public String getFullClassName(){
    StringBuffer sb = new StringBuffer();
   
    DmcTypeClassDefinitionREFMV objClass = (DmcTypeClassDefinitionREFMV) attributes.get(__objectClass.id);
    for(int i=0; i<objClass.getMVSize(); i++){
      if (i>0)
        sb.append(" " + objClass.getMVnth(i).getClassInfo().name);
      else
        sb.append(objClass.getMVnth(i).getClassInfo().name);
    }
   
    return(null);
  }
 
    /**
     * Adds the specified auxiliary class name to the object.
     * @param cd The auxiliary class name.
     * @throws DmcValueException 
     */
    public void addAux(String cd) throws DmcValueException {
      DmcTypeClassDefinitionREFMV ocl = (DmcTypeClassDefinitionREFMV) get(__objectClass.id);

    if (ocl != null)
      ocl.add(cd);
    }
   
    /**
     * Adds the specified auxiliary class name to the object.
     * @param cd The auxiliary class name.
     * @throws DmcValueException 
     */
    public void addAux(ClassDefinitionREF cd) throws DmcValueException {
      DmcTypeClassDefinitionREFMV ocl = (DmcTypeClassDefinitionREFMV) get(__objectClass.id);

    if (ocl != null)
      ocl.add(cd);
    }
   
    /**
     * Removes the specified auxiliary class name from the object.
     * @param cd The auxiliary class name.
     */
    public void removeAux(String cd){
      DmcTypeClassDefinitionREFMV ocl = (DmcTypeClassDefinitionREFMV) get(__objectClass.id);

    if (ocl != null)
      ocl.del(cd);
    }

    /**
     * Determines if the specified class is in our ocl.
     * @param cd The auxiliary class name.
     */
    public boolean hasAux(String cd){
      DmcTypeClassDefinitionREFMV ocl = (DmcTypeClassDefinitionREFMV) get(__objectClass.id);

    if (ocl == null)
      return(false);
   
    return(ocl.contains(cd));
    }

  ////////////////////////////////////////////////////////////////////////////////
  // Attribute access
 
  /**
   * Returns the holder of value for the named attribute. Use this with caution!
   * This is generally used only by derived wrapper classes of DmcObject.
   * @param name The name of the attribute.
   * @return DmcAttribute
   */
  public DmcAttribute<?> get(String name){
    DmcAttributeInfo ai = getAttributeInfo(name);
    if (ai == null)
      return(null);
    return (attributes.get(ai.id));
  }
 
  /**
   * Returns the holder of the value for the named attribute. Use this with caution!
   * This is generally used only by derived wrapper classes of DmcObject.
   * @param id the id of the attribute.
   * @return DmcAttribute
   */
  public DmcAttribute<?> get(Integer id){
    return (attributes.get(id));
  }
 
  /**
   * Returns the holder of the value for the named attribute. Use this with caution!
   * This is generally used only by derived wrapper classes of DmcObject.
   * @param ai the attribute info definition.
   * @return DmcAttribute
   */
  public DmcAttribute<?> get(DmcAttributeInfo ai){
    DmcAttribute<?> rc = attributes.get(ai.id);
   
    // If you ask for the attribute using its attribute info and we find it, we check to
    // see if it's set on the attribute. If not, we set it. This may seem weird, but it's
    // because the link to the DmcAttributeInfo is lost when DMOs are transported over
    // GWT's serialization mechanisms. However, when we use generated DMOs to access the
    // the attributes, THEY have the attribute info, and so we just set it back on the
    // attribute.
   
    if ( (rc != null) && (rc.getAttributeInfo() == null))
      rc.setAttributeInfo(ai);
   
    return (rc);
  }
 
  /**
   * Retrieves the names of attributes in this object. If you only want the single
   * valued attributes.
   * @param onlySV  if true, will only return single valued attribute names.
   * @return The set of attribute names.
   */
  public ArrayList<String> getAttributeNames(boolean onlySV){
    ArrayList<String>  names = new ArrayList<String>();
    for(DmcAttribute<?> attr: attributes.values()){
      if (onlySV){
        if (attr.getAttributeInfo().valueType == ValueTypeEnum.SINGLE){
          names.add(attr.getName());
        }
      }
      else
        names.add(attr.getName());
    }
    return(names);
  }
 
  /**
   * @return The attributes that comprise this object. USE THIS WITH CAUTION!
   */
  public Map<Integer,DmcAttribute<?>> getAttributes(){
    return(attributes);
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Attribute modification
 
  /**
   * This method sets the value of a single-valued attribute. If you had previously set the
   * same attribute to a different type, you get a class cast exception.
   * @param attrName  The attribute name.
   * @param attr      The attribute to be stored.
   * @throws DmcValueException
   */
  public <T extends DmcAttribute<?>> T set(String attrName, DmcAttribute<?> attr) throws DmcValueException {
    DmcAttributeInfo ai = getAttributeInfo(attrName);
   
    if (ai == null){
      DmcValueException dve = new DmcValueException("Invalid attribute: " + attrName + " for class: " + this.getClass().getName());
      throw(dve);
    }
   
    return(set(ai,attr));
  }
 
  /**
   * This method sets the value of a single-valued attribute.
   * @param ai    the attribute info.
   * @param attr  the attribute to be stored.
   * @throws DmcValueException
   */
  @SuppressWarnings("unchecked")
  public <T extends DmcAttribute<?>> T set(DmcAttributeInfo ai, DmcAttribute<?> attr) throws DmcValueException {   
    synchronized (attributes) {
      DmcAttribute<?> existing = attributes.get(ai.id);
     
      if (existing == null){
        attributes.put(ai.id, attr);
        attr.setAttributeInfo(ai);
      }
     
      // BIG NOTE: performing modification of an object and performing backref tracking
      // are MUTUALLY EXCLUSIVE behaviours. We don't want to track backrefs when we have
      // a modifier on an object because we would wind up tracking the references twice,
      // once while creating the modifier and again when the modifier is applied.
      if (getModifier() == null){
        // Also don't do backref tracking in staging mode
        if (supportsBackrefTracking() && !isStaging()){
          if (DmcOmni.instance().backRefTracking() && DmcOmni.instance().trackThisAttribute(attr.ID)){
            if (attr instanceof DmcTypeNamedObjectREF){
              // We're modifying a reference attribute, so track that puppy
              DmcObject obj = ((DmcObject)((DmcNamedObjectREF<?>)attr.getSV()).getObject());
              if (obj != null){
                Modifier backrefMod = new Modifier(ModifyTypeEnum.SET,attr,this);
                obj.addBackref(backrefMod);
               
                // Let the reference know the backref modifier - this allows us to
                // easily remove the backref if the reference is deleted
                ((DmcNamedObjectREF<?>)attr.getSV()).setBackrefModifier(backrefMod);
              }
            }
          }
        }
      }
      else{
        getModifier().add(new Modifier(ModifyTypeEnum.SET, attr));
      }
     
//    if ( (getContainer() != null) && (getContainer().getListenerManager() == null) ){
//      // TODO implement attribute change listener hooks
//    }
     
      return (T) (attr);
    }
  }
 
  /**
   * This method adds a value to a multi-valued attribute. If you had previously set the
   * same attribute to a different type, you get a class cast exception.
   * @param attrName  The attribute name.
   * @param attr      The attribute value to be stored.
   * @throws DmcValueException
   */
  public <T extends DmcAttribute<?>> T add(String attrName, DmcAttribute<?> attr) throws DmcValueException {
    DmcAttributeInfo ai = getAttributeInfo(attrName);
   
    if (ai == null){
      DmcValueException dve = new DmcValueException("Invalid attribute: " + attrName + " for class: " + this.getClass().getName());
      throw(dve);
    }
   
    return(add(ai,attr));
  }
 
  /**
   * This method adds a value to a multi-valued attribute. If you had previously set the
   * same attribute to a different type, you get a class cast exception.
   * @param ai   The attribute info.
   * @param attr The attribute value to be stored.
   * @throws DmcValueException
   */
  @SuppressWarnings("unchecked")
  public <T extends DmcAttribute<?>> T add(DmcAttributeInfo ai, DmcAttribute<?> attr) throws DmcValueException {
    synchronized (attributes) {
      DmcAttribute<?> existing = (DmcAttribute<?>) attributes.get(ai.id);
     
      if (existing == null){
        attributes.put(ai.id, attr);
        attr.setAttributeInfo(ai);
      }
     
      // BIG NOTE: performing modification of an object and performing backref tracking
      // are MUTUALLY EXCLUSIVE behaviours. We don't want to track backrefs when we have
      // a modifier on an object because we would wind up tracking the references twice,
      // once while creating the modifier and again when the modifier is applied.
      if (isModrec() == false){
        // Also don't do backref tracking in staging mode
        if (supportsBackrefTracking() && !isStaging()){
          // TODO: need to have the upper bound of the IDs for the meta schema available
          // so that we can check whether we want to track the back references.
          if (DmcOmni.instance().backRefTracking() && DmcOmni.instance().trackThisAttribute(attr.ID)){
            if ( (attr instanceof DmcTypeNamedObjectREF) && (getLastValue() != null)){
              DmcObject obj = ((DmcObject)((DmcNamedObjectREF<?>)getLastValue()).getObject());
              if (obj != null){
                // We're modifying a reference attribute, so track that puppy
                DmcAttribute<?> mod = attr.getNew();
                mod.setAttributeInfo(ai);
                mod.add(getLastValue());
               
                Modifier backrefMod = new Modifier(ModifyTypeEnum.ADD,mod,this);
                ((DmcObject)((DmcNamedObjectREF<?>)getLastValue()).getObject()).addBackref(backrefMod);
               
                // Let the reference know the backref modifier - this allows us to
                // easily remove the backref if the reference is deleted
                ((DmcNamedObjectREF<?>)getLastValue()).setBackrefModifier(backrefMod);
              }
            }
          }
        }
      }
     
      if (getModifier() != null){
        if (getLastValue() == null){
          // Last value can be null in the case of SET attributes since we don't
          // actually add a value to the SET if it already exists. However, in other
          // cases, this is a code error - so pitch a fit!
          if ( (ai.valueType != ValueTypeEnum.HASHMAPPED) && (ai.valueType != ValueTypeEnum.TREEMAPPED)){
            // This is okay
          }
          else if ( (ai.valueType != ValueTypeEnum.HASHSET) && (ai.valueType != ValueTypeEnum.TREESET)){
            throw(new IllegalStateException("Last value shouldn't be null."));
          }
        }
        else{
          // Get an attribute value holder of the same type and hang on to the last
          // value that was added to it
          DmcAttribute<?> mod = attr.getNew();
          mod.setAttributeInfo(ai);
         
          mod.add(getLastValue());
          getModifier().add(new Modifier(ModifyTypeEnum.ADD, mod));
        }
      }
     
     
//    if ( (getContainer() != null) && (getContainer().getListenerManager() == null) ){
//      // TODO implement attribute change listener hooks
//    }
 
      return (T) (attr);
    }
  }
 
  /**
   * This method deletes a value from a multi-valued attribute.
   * @param <T>        The class
   * @param attrName  The attribute name.
   * @param value     The value to be stored.
   * @throws DmcValueException
   * @throws DmcValueException
   */
  public <T extends DmcAttribute<?>> T del(String attrName, Object value){
    DmcAttributeInfo ai = getAttributeInfo(attrName);
   
    if (ai == null)
      return(null);
   
    return(del(ai,value));
  }
 
  /**
   * This method is called in generated DMOs to handle the case where we're
   * using an empty DMO to create a modifier. When we perform del(), we have
   * no attribute, so the DMO del() method constructs the DmcAttribute on our
   * behalf.
   * @param mod The appropriately typed attribute holder.
   * @param value The value to be deleted.
   */
  protected void delFromEmptyAttribute(DmcAttribute<?> mod, Object value){
    try {
      mod.add(value);
      getModifier().add(new Modifier(ModifyTypeEnum.DEL, mod));   
    } catch (DmcValueException e) {
      if ( (mod.getAttributeInfo().valueType == ValueTypeEnum.HASHMAPPED) ||
           (mod.getAttributeInfo().valueType == ValueTypeEnum.TREEMAPPED) ){
        throw(new IllegalStateException("Changes to the Modifier shouldn't throw an exception. This is a MAPPED attribute and typeCheck () should accept just the key value as well as the mapped type itself.", e));
      }
     
      throw(new IllegalStateException("Changes to the Modifier shouldn't throw an exception.", e));
    }
  }
 
  /**
   * This method deletes a value from a multi-valued attribute.
   * @param <T>        The class
   * @param ai    the attribute info.
   * @param value the value to be stored.
   * @throws DmcValueException
   * @throws DmcValueException
   */
  @SuppressWarnings("unchecked")
  public <T extends DmcAttribute<?>> T del(DmcAttributeInfo ai, Object value){
    synchronized (attributes) {
      DmcAttribute<?> attr = (DmcAttribute<?>) attributes.get(ai.id);
      DmcNamedObjectREF<?> ref = null;
     
      if (getModifier() != null){
        try {
          DmcAttribute<?> mod = attr.getNew();
          mod.setAttributeInfo(ai);
         
          mod.add(value);
          getModifier().add(new Modifier(ModifyTypeEnum.DEL, mod));
        } catch (DmcValueException e) {
          throw(new IllegalStateException("Changes to the Modifier shouldn't throw an exception.", e));
        }
      }
     
      Object rc = null;
      if ( (value instanceof DmcNamedObjectREF) && !(value instanceof DmcExtendedReferenceIF)){
        DmcNamedObjectREF<?> key = (DmcNamedObjectREF<?>)value;

        if (key.getObject() == null)
          rc = attr.del(key.getObjectName());
        else if (value instanceof DmcMappedAttributeIF)
          rc = attr.del(((DmcMappedAttributeIF)value).getKey());
        else
          rc = attr.del(key.getObject());
       
      }
      else{
        if ( (attr.getAttributeInfo().valueType == ValueTypeEnum.HASHSET) || (attr.getAttributeInfo().valueType == ValueTypeEnum.TREESET))
          rc = attr.del(value);
        else if (value instanceof DmcExtendedReferenceIF)
          rc = attr.del(value);
        else if (value instanceof DmcMappedAttributeIF)
          rc = attr.del(((DmcMappedAttributeIF)value).getKey());
        else if (value instanceof DmcNamedObjectIF)
          rc = attr.del(((DmcNamedObjectIF)value).getObjectName());
        else
          rc = attr.del(value);
      }
     
      if (rc instanceof DmcNamedObjectREF)
        ref = (DmcNamedObjectREF<?>) rc;
     
      // BIG NOTE: performing modification of an object and performing backref tracking
      // are MUTUALLY EXCLUSIVE behaviours. We don't want to track backrefs when we have
      // a modifier on an object because we would wind up tracking the references twice,
      // once while creating the modifier and again when the modifier is applied.
      if ((ref != null) && (isModrec() == false)){
        // Also don't do backref tracking in staging mode
        if (supportsBackrefTracking() && !isStaging()){
          // TODO: need to have the upper bound of the IDs for the meta schema available
          // so that we can check whether we want to track the back references.
          if (DmcOmni.instance().backRefTracking() && DmcOmni.instance().trackThisAttribute(attr.ID)){
            if (ref.getObject() != null){
              if (ref.getBackrefModifier() != null)
                ((DmcObject)ref.getObject()).removeBackref(ref.getBackrefModifier());
            }
          }
        }
      }
     
           
      // If we have no further elements in the multi-value attribute, remove it
      if (attr.getMVSize() == 0)
        rem(ai);
 
      return (T) (attr);
    }
  }
   
  /**
   * Removes the specified attribute from the object.
   * @param attrName The attribute name.
   * @throws DmcValueException
   */
  public <T extends DmcAttribute<?>> T rem(String attrName){
    DmcAttributeInfo ai = getAttributeInfo(attrName);
   
    if (ai == null)
      return(null);
   
    return(rem(ai));
  }
 
  /**
   * Removes the specified attribute from the object.
   * @param ai the attribute info.
   * @throws DmcValueException
   */
  @SuppressWarnings("unchecked")
  public <T extends DmcAttribute<?>> T rem(DmcAttributeInfo ai){
    synchronized (attributes) {
      T attr = (T) attributes.remove(ai.id);
     
      if (getModifier() != null){
        try {
          getModifier().add(new Modifier(ModifyTypeEnum.REM, ai));
        } catch (DmcValueException e) {
          throw(new IllegalStateException("Changes to the Modifier shouldn't throw an exception.", e));
        }
      }
     
      // BIG NOTE: performing modification of an object and performing backref tracking
      // are MUTUALLY EXCLUSIVE behaviours. We don't want to track backrefs when we have
      // a modifier on an object because we would wind up tracking the references twice,
      // once while creating the modifier and again when the modifier is applied.
//      if ((getModifier() == null) && (attr != null)){
      if ((isModrec() == false) && (attr != null)){
        // Also don't do backref tracking in staging mode
        if (supportsBackrefTracking() && !isStaging()){
          if (attr instanceof DmcTypeNamedObjectREF){
            ((DmcTypeNamedObjectREF<?,?>)attr).removeBackReferences();
//            // TODO: need to have the upper bound of the IDs for the meta schema available
//            // so that we can check whether we want to track the back references.
//            if (DmcOmni.instance().backRefTracking() && (attr.ID > 200)){
//              if (attr.attrInfo.valueType == ValueTypeEnum.SINGLE){
//                DmcNamedObjectREF ref = (DmcNamedObjectREF) attr.getSV();
//                if (ref.getObject() != null){
//                  if (ref.getBackrefModifier() != null)
//                    ((DmcObject)ref.getObject()).removeBackref(ref.getBackrefModifier());
//                }
//              }
//              else{
//                for(int i=0; i< attr.getMVSize(); i++){
//                  DmcNamedObjectREF ref = (DmcNamedObjectREF) attr.getMVnth(i);
//                  if ((ref != null) && (ref.getObject() != null)){
//                    if (ref.getBackrefModifier() != null)
//                      ((DmcObject)ref.getObject()).removeBackref(ref.getBackrefModifier());
//                  }
//                }
//              }
//            }
          }
        }
      }
     

     
//    if ( (getContainer() != null) && (getContainer().getListenerManager() != null)){
//      // TODO implement attribute change listener hooks
//    }
     
      return(attr);
    }
  }
 
  // Commented this out for now - shouldn't be used because of the complexity of having
  // to know the previous value for purposes of reference tracking.
//  /**
//   * This method adds a value to a multi-valued attribute. If you had previously set the
//   * same attribute to a different type, you get a class cast exception.
//   * @param attrName  The attribute name.
//   * @param attr      The attribute value to be stored.
//   * @throws DmcValueException
//   */
//  @SuppressWarnings("unchecked")
//  public <T extends DmcAttribute> T nth(String attrName, int index, DmcAttribute attr) throws DmcValueException {
//    DmcAttributeInfo ai = getAttributeInfo(attrName);
//   
//    if (ai == null){
//      DmcValueException dve = new DmcValueException("Invalid attribute: " + attrName + " for class: " + this.getClass().getName());
//      throw(dve);
//    }
//   
//    return(nth(ai,index,attr,null));
//  }
 
  /**
   * This method is called in generated DMOs to handle the case where we're
   * using an empty DMO to create a modifier. When we perform setNth(#,null),
   * we have no attribute, so we create a specialized modifier.
   * @param mod The appropriately typed attribute holder.
   * @param value The value to be deleted.
   */
  protected void nthNullFromEmptyAttribute(DmcAttributeInfo ai, int idx){
    try {
      getModifier().add(new Modifier(ModifyTypeEnum.NTH, ai, idx));   
    } catch (DmcValueException e) {
      throw(new IllegalStateException("Changes to the Modifier shouldn't throw an exception.", e));
    }
  }
 
  /**
   * This method adds a value to a multi-valued attribute. If you had previously set the
   * same attribute to a different type, you get a class cast exception.
   * @param ai   The attribute info.
   * @param attr The attribute value to be stored.
   * @throws DmcValueException
   */
  @SuppressWarnings("unchecked")
  protected <T extends DmcAttribute<?>> T nth(DmcAttributeInfo ai, int index, DmcAttribute<?> attr, Object previous) throws DmcValueException {
    synchronized (attributes) {
      DmcAttribute<?> existing = (DmcAttribute<?>) attributes.get(ai.id);
     
      if (existing == null){
        attributes.put(ai.id, attr);
        attr.setAttributeInfo(ai);
      }
     
      // BIG NOTE: performing modification of an object and performing backref tracking
      // are MUTUALLY EXCLUSIVE behaviours. We don't want to track backrefs when we have
      // a modifier on an object because we would wind up tracking the references twice,
      // once while creating the modifier and again when the modifier is applied.
//      if (getModifier() == null){
      if (isModrec() == false){
        // Also don't do backref tracking in staging mode
        if (supportsBackrefTracking() && !isStaging()){
          // TODO: need to have the upper bound of the IDs for the meta schema available
          // so that we can check whether we want to track the back references.
//          if (DmcOmni.instance().backRefTracking() && (attr.ID > 200)){
          if (DmcOmni.instance().backRefTracking() && DmcOmni.instance().trackThisAttribute(attr.ID)){
            if ( (attr instanceof DmcTypeNamedObjectREF) && (getLastValue() != null)){
              DmcObject obj = ((DmcObject)((DmcNamedObjectREF<?>)getLastValue()).getObject());
              if (obj != null){
                // We're modifying a reference attribute, so track that puppy
                DmcAttribute<?> mod = attr.getNew();
                mod.setAttributeInfo(ai);
                mod.add(getLastValue());
               
                Modifier backrefMod = new Modifier(ModifyTypeEnum.NTH,mod,this,index);
                ((DmcObject)((DmcNamedObjectREF<?>)getLastValue()).getObject()).addBackref(backrefMod);
               
                ((DmcNamedObjectREF<?>)getLastValue()).setBackrefModifier(backrefMod);
              }
            }
            if (attr instanceof DmcTypeNamedObjectREF){
              // And now some tricky stuff. Due to the fact that the nth() mechanisms can
              // result in a previous value being removed and replaced in one operation,
              // we have to take this into account when tracking back references. Any
              // previous value for this index is passed in to us from the generated
              // nth() method so that we can clean up the back reference if required.
              if (previous != null){
                DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>) previous;
                if (ref.getObject() != null){
                  if (ref.getBackrefModifier() != null)
                    ((DmcObject)ref.getObject()).removeBackref(ref.getBackrefModifier());
                }
              }
            }
          }
        }
      }
     
      if (getModifier() != null){
        if (getLastValue() == null){
          // Last value can be null in the case of indexed multi-valued attributes since this
          // is how we remove an indexed value.
          if ( (ai.indexSize == 0) || (ai.valueType != ValueTypeEnum.MULTI))
            throw(new IllegalStateException("Code gen error! The nth() interface is not applicable to attribute: " + ai.name + " of valueType: " + ai.valueType));
          getModifier().add(new Modifier(ModifyTypeEnum.NTH, ai, index));
        }
        else{
          // Get an attribute value holder of the same type and hang on to the last
          // value that was added to it
          DmcAttribute<?> mod = attr.getNew();
          mod.setAttributeInfo(ai);
         
          mod.setMVnth(index, getLastValue());
          getModifier().add(new Modifier(ModifyTypeEnum.NTH, mod, index));
        }
      }
     
     
      // If there are no longer values stored in the attribute, remove it
      if (!attr.hasValue())
        rem(ai);
     
//    if ( (getContainer() != null) && (getContainer().getListenerManager() == null) ){
//      // TODO implement attribute change listener hooks
//    }
     
      return (T) (attr);
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Object formatting
 
  /**
   * Returns the object in its native Object Instance Format.
   */
  public String toString(){
    return(toOIF(15));
  }
 
  /**
   * Returns the references to this object formatted as a String. This will only
   * return a value if you've turned on backref tracking via the DmcOmni. The output
   * will appear as follows:
   * <pre>
   * References to: obj1 (1395193582)
     *   (ObjWithRefs) obj3 via SV objRef
   * </pre>
   * The first line indicates the name of the object followed by the identity hash
   * code for the object, which can be useful when determining the actual object instance
   * you're referring to. The subsequent lines indicate the class of object doing the
   * referring, its name and the attribute via which the reference is maintained.
   */
  public String getBackRefs(){
    synchronized(attributes){   
      DmcTypeModifierMV mods = getBackref();
      if (mods == null)
        return("No backrefs to " + ((DmcNamedObjectIF)this).getObjectName()+ " (" + System.identityHashCode(this) + ")");
      else{
        StringBuffer sb = new StringBuffer();
        sb.append("References to: " + ((DmcNamedObjectIF)this).getObjectName() + " (" + System.identityHashCode(this)+ ")\n");
       
        Iterator<Modifier> modit = mods.getMV();
        if (modit != null){
          while(modit.hasNext()){
            Modifier mod = modit.next();
            DmcNamedObjectIF referrer   = mod.getReferringObject();
            DmcObject     obj    = (DmcObject) referrer;
            DmcAttribute<?> attr = mod.getAttribute();
            if (attr.getAttributeInfo().valueType == ValueTypeEnum.SINGLE)
              sb.append("  (" + obj.getConstructionClassName() + ") " + referrer.getObjectName() + " via SV " + attr.getName() + "\n");
            else{
              if (attr.getAttributeInfo().indexSize == 0)
                sb.append("  (" + obj.getConstructionClassName() + ") " +  referrer.getObjectName() + " via MV " + attr.getName() + "\n");
              else
                sb.append("  (" + obj.getConstructionClassName() + ") " +  referrer.getObjectName() + " via INDEX " + mod.getIndex() + " " + attr.getName() + "\n");
            }
          }
        }
        return(sb.toString());
      }
    }
  }
 
  /**
   * Returns the objects that are referring to this object. This will only
   * return a value if you've turned on backref tracking via the DmcOmni.
   */
  public ArrayList<DmcObject> getReferringObjects(){
    ArrayList<DmcObject>  rc = new ArrayList<DmcObject>();
   
    synchronized(attributes){
      DmcTypeModifierMV mods = getBackref();
      if (mods != null){
        Iterator<Modifier> modit = mods.getMV();
        if (modit != null){
          while(modit.hasNext()){
            Modifier mod = modit.next();
            rc.add((DmcObject)mod.getReferringObject());
          }
        }
      }
      return(rc);
    }
  }
 
  /**
   * Returns the objects that are referring to this object via the specified attribute. This will only
   * return a value if you've turned on backref tracking via the DmcOmni.
   */
  public ArrayList<DmcObject> getReferringObjectsViaAttribute(DmcAttributeInfo ai){
    ArrayList<DmcObject>  rc = new ArrayList<DmcObject>();
   
    synchronized(attributes){
      DmcTypeModifierMV mods = getBackref();
      if (mods != null){
        Iterator<Modifier> modit = mods.getMV();
        if (modit != null){
          while(modit.hasNext()){
            Modifier mod = modit.next();
            if (mod.getAttributeID() == ai.id)
              rc.add((DmcObject)mod.getReferringObject());
          }
        }
      }
      return(rc);
    }
  }
 
  /**
   * Returns the object in Object Instance Format (OIF). The attribute values
   * will be automatically aligned beyond the length of the longest attribute
   * name.
   * @return The String representation of the object.
   */
  public String toOIF(){
    synchronized (attributes) {
      int longest = 0;
      for(DmcAttribute<?> attr : attributes.values()){
        if (attr.ID == __objectClass.id)
          continue;
        if (attr.getName().length() > longest)
          longest = attr.getName().length();
      }
 
      return(toOIF(longest+2));
    }
  }
 
  /**
   * Returns the object in Object Instance Format (OIF).
   * @return The String representation of the object.
   */
  public String toOIFNoPadding(){
    synchronized (attributes) {
      StringBuffer  sb = new StringBuffer();
     
      appendClassNames(sb);
     
      if (getModifier() == null){
        // Dump the attribute values
        for(DmcAttribute<?> attr : attributes.values()){
          if ( attr.getID() != __objectClass.id )
            attr.toOIF(sb);
        }
      }
      else{
        // If we have a naming attribute and it's available, dump it
        if ( (getConstructionClassInfo() != null) && (getConstructionClassInfo().nameAttribute != null) ){
          DmcAttribute<?> attr = get(getConstructionClassInfo().nameAttribute);
          if (attr != null)
            attr.toOIF(sb);
        }
        // Just dump the modifier not the attributes
        getModifier().toOIF(sb);
      }
 
      return(sb.toString());
    }
  }
 
  /**
   * Returns the object in Object Instance Format (OIF) with the attribute name left
   * justified in an amount of space indicated by padding. This just provides a more
   * readable version of the object.
   * @param padding The amount of space in which to display the attribute names.
   * @return The String representation of the object.
   */
  public String toOIF(int padding){
    StringBuffer  sb = new StringBuffer();
   
    appendClassNames(sb);
   
    if (getModifier() == null){
      // Dump the attribute values
      TreeMap<String,DmcAttribute<?>> sorted = new TreeMap<String, DmcAttribute<?>>();
     
      for(DmcAttribute<?> attr : attributes.values())
        sorted.put(attr.getName(), attr);
     
      for(DmcAttribute<?> attr : sorted.values()){
        if ( attr.getID() != __objectClass.id )
          attr.toOIF(sb,padding);
      }
    }
    else{
      // If we have a naming attribute and it's available, dump it
      if ( (getConstructionClassInfo() != null) && (getConstructionClassInfo().nameAttribute != null) ){
        DmcAttribute<?> attr = get(getConstructionClassInfo().nameAttribute);
        if (attr != null)
          attr.toOIF(sb, padding);
      }
     
      // Just dump the modifier, not the attributes
      if (getModifier().getMVSize() > 0)
        getModifier().toOIF(sb, padding);
      else
        sb.append("* no modifications\n");
    }
   

    return(sb.toString());
  }
 
  ///////////////////////////////////////////////////////////////////////////
  // JSON formatting
 
  public String toJSON(){
    StringBuffer sb = new StringBuffer();
   
    toJSON(sb,0,"");
   
    return(sb.toString());
  }
 
  public String toJSON(int padding, String indent){
    StringBuffer sb = new StringBuffer();
   
    toJSON(sb,padding,indent);
   
    return(sb.toString());
  }
 
  public void toJSON(StringBuffer sb, int padding, String indent){
    String indentPlus = indent + "  ";
   
    sb.append(indent + "{ \"" + getConstructionClassName() + "\": {\n");
   
        // We attempt to dump the object class information first. This
        // isn't guaranteed when we receive a JSON object parsing, but
        // we do it here so that it's obvious what type of object you're
        // dealing with.
        DmcAttribute<?> oc = get(__objectClass.id);
       
        if (oc != null){
        sb.append(indentPlus + "  \"objectClass\": [\n" );
        oc.formatValueAsJSON(sb, padding, indentPlus + "  ");
        sb.append("\n" + indentPlus + "  ]");
       
          sb.append(",\n");
        }
       
        Iterator<DmcAttribute<?>> it = attributes.values().iterator();
        while(it.hasNext()){
          DmcAttribute<?> attr = it.next();

            if (attr.getID() == __objectClass.id){
            if (!it.hasNext()){
             
              // The object class is the last attribute, so get rid
              // of the extraneous ,\n
              sb.deleteCharAt(sb.length()-1);
              sb.deleteCharAt(sb.length()-1);
               }
            continue;
          }

          attr.toJSON(sb, padding, indentPlus + "  ");
         
            if (it.hasNext()){
              sb.append(",\n");
            }
        }
  
//        sb.append("\n" + indentPlus + "}");
       
        sb.append(indent + "\n}}");

  }
 
  public String toCompactJSON(){
    StringBuffer sb = new StringBuffer();
    toCompactJSON(sb);
    return(sb.toString());
  }
 
  public void toCompactJSON(StringBuffer sb){
    sb.append("{\"" + getConstructionClassName() + "\":{");
   
        sb.append("{");
       
        // We attempt to dump the object class information first. This
        // isn't guaranteed when we receive a JSON object parsing, but
        // we do it here so that it's obvious what type of object you're
        // dealing with.
        DmcAttribute<?> oc = get(__objectClass.id);
       
        if (oc != null){
        sb.append("\"objectClass\":[");
        oc.formatValueAsCompactJSON(sb);
        sb.append("]");
       
          sb.append(",");
        }
       
        Iterator<DmcAttribute<?>> it = attributes.values().iterator();
        while(it.hasNext()){
          DmcAttribute<?> attr = it.next();

            if ( attr.getName().equals(__objectClass.name)){
            if (!it.hasNext()){
             
              // The object class is the last attribute, so get rid
              // of the extraneous ,
              sb.deleteCharAt(sb.length()-1);
               }
            continue;
          }

          attr.toCompactJSON(sb);
         
            if (it.hasNext()){
              sb.append(",");
            }
        }
  
        sb.append("}");

        sb.append("}");
  }
 
 
 
  /**
   * A convenience method to display the class information for an object.
   * @param sb The buffer we append to.
   */
  private void appendClassNames(StringBuffer sb){
    if (getModifier() != null)
      return;
   
    DmcTypeClassDefinitionREFMV classes = (DmcTypeClassDefinitionREFMV) this.get(__objectClass.id);
   
    // Dump the construction class and any auxiliary classes
    if (classes != null){
      Iterator<ClassDefinitionREF> cls = classes.getMV();
      while(cls.hasNext()){
        ClassDefinitionREF cdr = cls.next();
        sb.append(cdr.getObjectName());
        if (cls.hasNext())
          sb.append(" ");
      }
      sb.append("\n");
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Object reference resolution
 
  /**
   * This method is generally called by code that uses a DmoObjectFactory to create DMO
   * instances. At this level, there is no concept of schemas, merely named objects. All
   * this method does is attempt to resolve references to named objects using the provided
   * name resolver.
   * @param rx A name resolver.
   * @throws Exception
   */
  public void resolveReferences(DmcNameResolverIF rx) throws DmcValueExceptionSet {
    resolveReferences(rx, false);
  }
 
  /**
   * This method is used to resolve references in environments where the complete schema
   * and its associated ClassDefinition objects are not available e.g. in GWT UIs. Resolution
   * is performed as usual, but we skip the object class.
   * @param rx A name resolver.
   * @throws DmcValueExceptionSet
   */
  public void resolveReferencesExceptClass(DmcNameResolverIF rx) throws DmcValueExceptionSet {
    resolveReferences(rx, true);
  }
 
  /**
   * We perform object resolution and optionally skip the class attribute.
   * @param rx A name resolver.
   * @param skipClass Indicates whether we should skip the class attribute or not.
   * @throws Exception
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public void resolveReferences(DmcNameResolverIF rx, boolean skipClass) throws DmcValueExceptionSet {
    synchronized (attributes) {
      DmcValueExceptionSet  errors = null;
 
      for(DmcAttribute<?> attr : attributes.values()){
        if (skipClass && (attr.ID == __objectClass.id))
          continue;
       
        // This ensures that we have the attribute info set. This is a temporary
        // hack. The actual solution is to make attrInfo private and always go through
        // the getAttributeInfo() method to get it.
        attr.getAttributeInfo();
         
        if (attr instanceof DmcTypeNamedObjectREF){
//System.out.println("DmcObject.resolveReferences() resolving: " + attr.getName());

          DmcTypeNamedObjectREF reference = (DmcTypeNamedObjectREF) attr;
         
          if (attr.getMVSize() == 0){
            DmcNamedObjectREF ref = (DmcNamedObjectREF) attr.getSV();
            if (ref.isResolved())
              continue;
           
            DmcNamedObjectIF    obj       = rx.findNamedObject(ref.getObjectName(),attr.ID);
            DmcObject       resolvedObject   = null;
           
            if (obj == null){
              DmcValueException ex = new DmcValueException("Could not resolve reference to: " + ref.getObjectName() + " via attribute: " + attr.getName());
              if (errors == null)
                errors = new DmcValueExceptionSet();
              errors.add(ex);
              continue;
            }
            else{
              try{
                if (obj instanceof DmcContainerIF){
                  resolvedObject = ((DmcContainerIF)obj).getDmcObject();
                  ref.setObject((DmcNamedObjectIF) ((DmcContainerIF)obj).getDmcObject());
                }
                else{
                  resolvedObject = (DmcObject)obj;
                  ref.setObject(obj);
                }
              }
              catch(ClassCastException e){
                DmcValueException ex = new DmcValueException("Attribute " + attr.getName() + " is of type: " + attr.getAttributeInfo().type + " and you've tried to set it to " + obj.getObjectName() + " which is of type " + resolvedObject.getConstructionClassName());
                if (errors == null)
                  errors = new DmcValueExceptionSet();
                errors.add(ex);
                continue;
              }
            }
           
            // NOTE: we wouldn't do the backref tracking in the case of DMP
            // messages (which are marked as supportsBackrefTracking false).
            if (supportsBackrefTracking()){
//              if (DmcOmni.instance().backRefTracking() && (attr.ID > 200)){
              if (DmcOmni.instance().backRefTracking() && DmcOmni.instance().trackThisAttribute(attr.ID)){
                Modifier backrefMod = new Modifier(ModifyTypeEnum.SET,attr,this);
                resolvedObject.addBackref(backrefMod);
               
                // Let the reference know the backref modifier - this allows us to
                // easily remove the backref if the reference is deleted
                ref.setBackrefModifier(backrefMod);
              }
            }

          }
          else{
            Iterator<DmcNamedObjectREF> refs = reference.getMV();
            if (refs != null){
              int currIndex = -1;
              while(refs.hasNext()){
                currIndex++;
                DmcNamedObjectREF ref = refs.next();
               
                // Note: ref may be null if this is an indexed attribute and
                // there is no value at the current index
                if ( (ref == null) || ref.isResolved())
                  continue;
               
                DmcNamedObjectIF    obj       = rx.findNamedObject(ref.getObjectName(),attr.ID);
                DmcObject       resolvedObject   = null;
               
                if (obj == null){
                  DmcValueException ex = new DmcValueException("Could not resolve reference to: " + ref.getObjectName() + " via attribute: " + attr.getName());
                  if (errors == null)
                    errors = new DmcValueExceptionSet();
                  errors.add(ex);
                  continue;
                }
                else{
                  try{
                    if (obj instanceof DmcContainerIF){
                      resolvedObject = ((DmcContainerIF)obj).getDmcObject();
                      ref.setObject((DmcNamedObjectIF) ((DmcContainerIF)obj).getDmcObject());
                    }
                    else{
                      resolvedObject = (DmcObject)obj;
                      ref.setObject(obj);
                    }
                  }
                  catch(ClassCastException e){
                    DmcValueException ex = new DmcValueException("Attribute " + attr.getName() + " is of type: " + attr.getAttributeInfo().type + " and you've tried to set it to " + obj.getObjectName() + " which is of type " + resolvedObject.getConstructionClassName());
                    if (errors == null)
                      errors = new DmcValueExceptionSet();
                    errors.add(ex);
                    continue;
                  }
                 
                  if (resolvedObject == null)
                    System.out.println("HERE");
                 
                 
                  // NOTE: we wouldn't do the backref tracking in the case of DMP
                  // messages (which are marked as supportsBackrefTracking false).
                  if (supportsBackrefTracking()){
//                    if (DmcOmni.instance().backRefTracking() && (attr.ID > 200)){
                    if (DmcOmni.instance().backRefTracking() && DmcOmni.instance().trackThisAttribute(attr.ID)){
                      if (attr.getAttributeInfo().indexSize == 0){
                        // We're modifying a reference attribute, so track that puppy
                        DmcAttribute<?> mod = attr.getNew();
                        mod.setAttributeInfo(attr.getAttributeInfo());
                        try {
                          if (ref instanceof DmcExtendedReferenceIF)
                            mod.add(ref);
                          else
                            mod.add(resolvedObject);
                        } catch (DmcValueException e) {
                          throw(new IllegalStateException("Creating backref for MV attribute during object resolution shouldn't throw exception.",e));
                        }
                       
                        Modifier backrefMod = new Modifier(ModifyTypeEnum.ADD,mod,this);
                        resolvedObject.addBackref(backrefMod);
                       
                        // Let the reference know the backref modifier - this allows us to
                        // easily remove the backref if the reference is deleted
                        ref.setBackrefModifier(backrefMod);
                      }
                      else{
                        DmcAttribute<?> mod = attr.getNew();
                        mod.setAttributeInfo(attr.getAttributeInfo());
                        try {
                          if (ref instanceof DmcExtendedReferenceIF)
                            mod.setMVnth(currIndex, null);
                          else
                            mod.setMVnth(currIndex, null);
                        } catch (DmcValueException e) {
                          throw(new IllegalStateException("Creating backref for indexed attribute during object resolution shouldn't throw exception.",e));
                        }
                       
                        Modifier backrefMod = new Modifier(ModifyTypeEnum.NTH,mod,this,currIndex);
                        resolvedObject.addBackref(backrefMod);
                       
                        ref.setBackrefModifier(backrefMod);
                      }
                    }
                  }
                }
              }
            }
          }
        }
        else if (attr instanceof DmcTypeComplexTypeWithRefs){
          try {
            ((DmcTypeComplexTypeWithRefs)attr).resolve(rx, attr.getName());
          } catch (DmcValueException e) {
            if (errors == null)
              errors = new DmcValueExceptionSet();
            errors.add(e);
          }
        }
      }
     
      if (errors != null)
        throw(errors);
    }
  }
 
//  /**
//   * We perform object resolution and optionally skip the class attribute.
//   * @param rx A name resolver.
//   * @param skipClass Indicates whether we should skip the class attribute or not.
//   * @throws Exception
//   */
//  @SuppressWarnings("unchecked")
//  public void resolveReferencesWithBackrefs(DmcNameResolverIF rx, boolean skipClass) throws DmcValueExceptionSet {
//    synchronized (attributes) {
//      DmcValueExceptionSet  errors = null;
// 
//      for(DmcAttribute attr : attributes.values()){
//        if (skipClass && (attr.ID == __objectClass.id))
//          continue;
//         
//        if (attr instanceof DmcTypeNamedObjectREF){
//          DmcTypeNamedObjectREF ref = (DmcTypeNamedObjectREF) attr;
////          try {
////            ref.resolveReferences(rx);
//            ref.doLazyResolution(this);
////          } catch (DmcValueException e) {
////            if (errors == null)
////              errors = new DmcValueExceptionSet();
////            errors.add(e);
////          }
//        }
//        else if (attr instanceof DmcTypeComplexTypeWithRefs){
//          try {
//            ((DmcTypeComplexTypeWithRefs)attr).resolve(rx, attr.getName());
//          } catch (DmcValueException e) {
//            if (errors == null)
//              errors = new DmcValueExceptionSet();
//            errors.add(e);
//          }
//        }
//      }
//     
//      if (errors != null)
//        throw(errors);
//    }
//  }
 
  public DmcObject cloneIt(){
    synchronized (attributes) {
      // Get a derived object of the right type
      DmcObject rc = getNew();
     
      try {
        DmcAttribute<?> ocl = get(__objectClass.id);
        if (ocl != null){
          // HACK - attrInfo
          ocl.getAttributeInfo();
          rc.add(__objectClass, ocl.cloneIt());
        }
        for(DmcAttribute<?> attr : attributes.values()){
          // HACK - attrInfo
          attr.getAttributeInfo();
          DmcAttribute<?> copy = attr.cloneIt();
          rc.add(copy.getAttributeInfo(), copy);
        }
      } catch (DmcValueException e) {
        throw(new IllegalStateException("DmcObject cloning shouldn't throw an exception.", e));
      }
     
      return(rc);
    }
  }
 
  /**
   * @return a shallow copy of this object.
   */
  public DmcObject shallowCopy(){
    synchronized (attributes) {
      // Get a derived object of the right type
      DmcObject rc = getNew();
     
      for(DmcAttribute<?> attr : attributes.values()){
        rc.attributes.put(attr.ID, attr);
      }
     
      return(rc);
    }
  }
 
  /**
   * This method will take the specified slice of attributes out of this object and
   * populate the object that holds the slice.
   * @param sliceContainer the container that will get the slice of attributes if they exist.
   * @param info The specification of the slice.
   */
  protected void populateSlice(DmcObject sliceContainer, DmcSliceInfo info){
    synchronized (attributes) {
      if (this instanceof DmcNamedObjectIF){
        // We also take the name attribute if this is a named object
        DmcAttribute<?> na = ((DmcNamedObjectIF)this).getObjectNameAttribute();
        sliceContainer.attributes.put(na.ID, na);
      }
     
      for(Integer id: info.attrIDs){
        DmcAttribute<?> attr = attributes.get(id);
        if (attr != null)
          sliceContainer.attributes.put(id, attr);
      }
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Object modification
 
  /**
   * This method applies the modification operations defined in the modifier to this object.
   * @param mods The modifications to be applied.
   * @throws DmcValueExceptionSet
   * @throws DmcValueException
   * @return true if any changes were made to the object and false otherwise.
   */
  @SuppressWarnings("unchecked")
  public boolean applyModifier(DmcTypeModifier mods) throws DmcValueExceptionSet, DmcValueException {
    synchronized (attributes) {
      boolean anyChange = false;
     
      // Check that the modifier is resolved
      mods.resolved();
     
      DmcAttribute<?> existing = null;
     
      Iterator<Modifier> modifiers = mods.getMV();
      while(modifiers.hasNext()){
        Modifier mod = modifiers.next();
        existing = get(mod.getAttributeName());
       
        // HACK: have to rationalize the attrInfo thing
        if (existing != null)
          existing.getAttributeInfo();
       
        switch(mod.getModifyType()){
        case ADD:
          if (existing == null){
            Iterator<Object> it = (Iterator<Object>) mod.getAttribute().getMV();
            Object value = it.next();
            if (value instanceof DmcNamedObjectREF){
              // we do things a little different for object refs because it relies on the lastValue
              // to create the backref modifier
              existing = mod.getAttribute().getNew();
             
              DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>)value;
              Object lastValue = existing.add(ref);
              setLastValue(lastValue);
             
              add(existing.getAttributeInfo(),existing);
            }
            else{
              // NOTE: we add a clone of the attribute since, if we don't, we wind
              // up storing the attribute instance that's in the modifier and adding
              // stuff to it!
              add(mod.getAttributeName(), mod.getAttribute().cloneIt());
            }
            anyChange = true;
          }
          else{
            Iterator<Object> it = (Iterator<Object>) mod.getAttribute().getMV();
            Object value = it.next();
            // NOTE: there will only ever be one value in the attribute and we have
            // to use an Iterator to get the value out.
 
            if ((value instanceof DmcNamedObjectREF) && !(value instanceof DmcExtendedReferenceIF)){
              // If the attribute is an object reference, we have to determine
              // whether we have the object or just its name - and perform the
              // add() accordingly.
              DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>)value;
              Object lastValue = existing.add(ref);
             
              if (lastValue != null){
                setLastValue(lastValue);
                anyChange = true;
                add(existing.getAttributeInfo(),existing);
              }
            }
            else{
              if ( existing.add(value) != null)
                anyChange = true;
            }
          }
          break;
        case DEL:
          if (existing == null){
            // The attribute being modified doesn't exist
            // TODO what to do with the deletion of a value from a non-existent attribute???
          }
          else{
            // NOTE: there will only ever be one value in the attribute and we have
            // to use an Iterator to get the value out.
            Iterator<Object> it = (Iterator<Object>) mod.getAttribute().getMV();
            Object value = it.next();
           
            if ( (value instanceof DmcNamedObjectREF) && !(value instanceof DmcExtendedReferenceIF)){           
              // If the attribute is an object reference, we have to determine
              // whether we have the object or just its name - and perform the
              // del() accordingly.
              DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>)value;
             
              setLastValue(ref);
              anyChange = true;
              del(existing.getAttributeInfo(), value);
            }
            else{
              if (value instanceof DmcMappedAttributeIF){
                if (existing.del(((DmcMappedAttributeIF)value).getKey()) != null)
                  anyChange = true;
              }

              else if ( existing.del(value) != null){
                anyChange = true;
              }
            }
           
            if (existing.getMVSize() == 0){
              rem(existing.getAttributeInfo());
            }
          }
          break;
        case SET:
          if (existing == null){
            set(mod.getAttributeName(),mod.getAttribute());
            anyChange = true;
          }
          else{
            Object value = mod.getAttribute().getSV();
           
            if (value instanceof DmcNamedObjectREF){
              // We had a value in this attribute to start with, so clean
              // up the back reference before we apply the new value
              ((DmcTypeNamedObjectREF<?, ?>)existing).removeBackReferences();
             
              DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>)value;
              if (existing.set(ref) != null){
                set(existing.getAttributeInfo(),existing);
                anyChange = true;
              }
            }
            else{
              if (existing.set(mod.getAttribute().getSV()) != null)
                anyChange = true;
            }
          }
          break;
        case NTH:
          // When a value is nulled, the modifier just contains the attribute info, not
          // a value holding attribute.
          int index = mod.getIndex();
          Object value = null;
         
          if (mod.getAttribute() != null)
            value = mod.getAttribute().getMVnth(index);
         
          if (existing == null){
            // NOTE: we add a clone of the attribute since, if we don't, we wind
            // up storing the attribute instance that's in the modifier and adding
            // stuff to it!
            if (value != null){
              setLastValue(value);
              nth(mod.getAttribute().getAttributeInfo(),index,mod.getAttribute().cloneIt(),null);
              anyChange = true;
            }
          }
          else{
            Object previous = existing.getMVnth(index);
           
            if (value == null){
              // We're removing the value at the current slot
             
              // We currently have a value and we're nulling it so there's been a change
              if (previous != null){
                existing.setMVnth(index, value);
                setLastValue(value);
                nth(existing.getAttributeInfo(), index, existing, previous);
                anyChange = true;
              }
            }
            else{

//              if ((value instanceof DmcNamedObjectREF) && !(value instanceof DmcExtendedReferenceIF)){
              if (value instanceof DmcNamedObjectREF){
                // If the attribute is an object reference, we have to determine
                // whether we have the object or just its name - and perform the
                // add() accordingly.
                DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>)value;
               
                if (existing.setMVnth(index, ref) != null){
                  setLastValue(ref);
                  nth(existing.getAttributeInfo(), index, existing, previous);
                  anyChange = true;
                }
               
              }
              else{
                if ( existing.setMVnth(index,value) != null)
                  anyChange = true;
              }
             
            }
           
            if (!existing.hasValue()){
              rem(existing.getAttributeInfo());
            }

          }
          break;
        case REM:
          if (rem(mod.getAttributeName()) != null)
            anyChange = true;
          break;
        }
      }
     
      return(anyChange);
    }
  }
 
  ///////////////////////////////////////////////////////////////////////////
  // Extensible object support
 
  /**
   * If this object has a class type of EXTENSIBLE, we return the set of
   * attributes that are not part of the class or its auxiliary classes
   * i.e. the additional attributes.
   * @return null if this object is not extensible or the additional attributes otherwise.
   */
  public Map<Integer, DmcAttribute<?>> getAdditionalAttributes(){
    if (getConstructionClassInfo().classType == ClassTypeEnum.EXTENSIBLE){
      TreeMap<Integer,DmcAttribute<?>> map = new TreeMap<Integer, DmcAttribute<?>>();
     
      for(DmcAttribute<?> attr: attributes.values()){
        if (!allowsAttribute(attr.getAttributeInfo())){
          map.put(attr.ID, attr);
        }
      }
     
      return(map);
    }
    else{
      return(null);
    }
  }
 
  ////////////////////////////////////////////////////////////////////////////////
  // Object serialization
 
  /**
   * Returns the data type of our construction class if we're in an operational context
   * where the underlying class definition is available; otherwise, UNKNOWN is returned.
   */
  public DataTypeEnum getDataType(){
    DataTypeEnum rc = DataTypeEnum.UNKNOWN;
   
    DmcTypeClassDefinitionREFMV objClass = (DmcTypeClassDefinitionREFMV) attributes.get(__objectClass.id);
    if (objClass != null){
      ClassDefinitionREF ref = objClass.getMVnth(0);
      if (ref.getObject() != null){
        ClassDefinitionDMO dmo = ref.getObject();
        rc = dmo.getDataType();
      }
    }
     
    return(rc);
  }
 
    /**
     * Serializes the object using Dark Matter serialization techniques.
     * @param dos
     * @throws IOException
     * @throws DmcValueException 
     */
    public void serializeIt(DmcOutputStreamIF dos) throws Exception, DmcValueException {
    synchronized (attributes) {
      // WRITE: the objectClass
      DmcTypeClassDefinitionREFMV oc = (DmcTypeClassDefinitionREFMV) get(__objectClass.id);
      // WRITE: the object class is serialized as [class count] id id id...
      dos.writeInt(oc.getMVSize());
      for(int i=0; i<oc.getMVSize(); i++){
        dos.writeInt(oc.getMVnth(i).getClassInfo().id);
      }
 
      // We have to determine the number of attributes we're going to write. That will
      // depend on the dataType of the attributes and the mode that the output stream
      // is in.
      //
      // We never write the objectClass attribute or count it towards the number of attributes
      // to be serialized.
      int attrcount = 0;
      Iterator<DmcAttribute<?>> it = attributes.values().iterator();
      while (it.hasNext()) {
        DmcAttribute<?> attr = it.next();
        if (attr.getID() != __objectClass.id){
          if (attr.getAttributeInfo().dataType == DataTypeEnum.TRANSIENT)
            continue;
          if (dos.isFile()){
            if (attr.getAttributeInfo().dataType == DataTypeEnum.PERSISTENT)
              attrcount++;
          }
          else
            attrcount++;
        }
      }
     
      // WRITE: the number of attributes
      dos.writeAttributeCount(attrcount);
 
      it = attributes.values().iterator();
      while (it.hasNext()) {
        DmcAttribute<?> attr = it.next();
       
        if (attr.getID() != __objectClass.id){
          if (attr.getAttributeInfo().dataType == DataTypeEnum.TRANSIENT)
            continue;
          if (dos.isFile()){
            if (attr.getAttributeInfo().dataType == DataTypeEnum.PERSISTENT)
              attr.serializeIt(dos);
          }
          else
            attr.serializeIt(dos);
        }
      }
    }
    }
   
    public void deserializeIt(DmcInputStreamIF disthrows Exception {
      int attrCount = dis.readAttributeCount();
     
      for(int i=0; i<attrCount; i++){
        int attrID = dis.readAttributeID();
        DmcAttribute<?> attr = null;
        try{
          attr = dis.getAttributeInstance(attrID);
         
          if (attr instanceof DmcTypeNamedObjectREF<?,?>){
           
          }
        }
        catch(Exception ex){
          throw(new IllegalStateException("While decoding: " + getConstructionClassName(), ex));
        }
        attr.deserializeIt(dis);
        add(attr.getAttributeInfo(), attr);
      }
    }
   
    ///////////////////////////////////////////////////////////////////////////
   
    /**
     * This method performs an attribute by attribute comparison of this
     * object with the specified object and returns true if they are equal.
     */
    @Override
    public boolean equals(Object obj){
    synchronized (attributes){   
        boolean rc = true;
       
        if (obj instanceof DmcObject){
          DmcObject other = (DmcObject) obj;
         
          // If the construction class names don't match, we fail immediately
          if (getConstructionClassName().equals(other.getConstructionClassName())){
            // If the number of attributes is different, they're not equal
            if (attributes.size() != other.attributes.size())
              return(false);
           
            // Cycle through each attribute
            for(DmcAttribute<?> attr: attributes.values()){
              DmcAttribute<?> otherAttr = other.get(attr.attrInfo);
             
              // If we have an attribute that isn't in the other object - we're not equal
              if (otherAttr == null){
                rc = false;
                break;
              }
             
              if (attr.attrInfo.valueType == ValueTypeEnum.SINGLE){
                if (!attr.getSV().equals(otherAttr.getSV())){
                  rc = false;
                  break;
                }
              }
              else{
                // If the value counts don't align - we're not equal
                if (attr.getMVSize() != otherAttr.getMVSize()){
                  rc = false;
                  break;
                }
                else{
                  if (attr.attrInfo.indexSize != 0){
                    // We have an indexed attribute
                    for(int i=0; i<attr.getMVSize(); i++){
                      Object thisval = attr.getMVnth(i);
                      Object thatval = otherAttr.getMVnth(i);
                     
                      if (thisval == null){
                        if (thatval != null){
                          rc = false;
                          break;
                        }
                        // Both indices are null - that's fine
                      }
                      else{
                        if (thatval == null){
                          rc = false;
                          break;
                        }
                        if (!thisval.equals(thatval)){
                          rc = false;
                          break;
                        }
                      }
                     
                    }
                  }
                  else{
                    switch(attr.attrInfo.valueType){
                    case HASHMAPPED:
                    case TREEMAPPED:
                      @SuppressWarnings("unchecked")
                    Iterator<Object> vals = (Iterator<Object>) attr.getMV();
                      while(vals.hasNext()){
                        DmcMappedAttributeIF thisval = (DmcMappedAttributeIF) vals.next();
                        DmcMappedAttributeIF otherval = (DmcMappedAttributeIF) otherAttr.getByKey(thisval.getKey());
                        if (otherval == null){
                          // We don't have the key in the other attr - we're not equal
                          rc = false;
                          break;
                        }
                       
                        if (!thisval.valuesAreEqual(otherval)){
                          // The values at the key don't match, we're not equal
                          rc = false;
                          break;
                        }
                      }
                      break;
                    case HASHSET:
                    case TREESET:
                      @SuppressWarnings("unchecked")
                    Iterator<Object> setvals = (Iterator<Object>) attr.getMV();
                      while(setvals.hasNext()){
                        Object thisval = setvals.next();
                        if (!otherAttr.contains(thisval)){
                          rc = false;
                          break;
                        }
                      }
                      break;
                    case MULTI:
                      for(int i=0; i<attr.getMVSize(); i++){
                        Object thisval = attr.getMVnth(i);
                        Object thatval = otherAttr.getMVnth(i);
                        if (!thisval.equals(thatval)){
                          rc = false;
                          break;
                        }
                      }
                      break;
                    }
                  }
                }
              }
             
              if (!rc)
                break;
            }
          }
        }
        else
          rc = false;
       
        return(rc);
    }
    }

    ///////////////////////////////////////////////////////////////////////////
   
    /**
     * This method should be used with extreme caution! It will forceably unresolve all
     * reference attributes on the object and clear any back reference tracking information.
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
  public void clearReferenceInfo(){
    for(DmcAttribute<?> attr : attributes.values()){
      if (attr instanceof DmcTypeNamedObjectREF){
        DmcTypeNamedObjectREF reference = (DmcTypeNamedObjectREF) attr;
        if (attr.getMVSize() == 0){
          DmcNamedObjectREF<?> ref = (DmcNamedObjectREF<?>) attr.getSV();
          ref.setObject(null);
        }
        else{
          Iterator<DmcNamedObjectREF<?>> refs = reference.getMV();
          if (refs != null){
            int currIndex = -1;
            while(refs.hasNext()){
              currIndex++;
              DmcNamedObjectREF<?> ref = refs.next();
              if (ref != null){
                ref.setObject(null);
              }
            }
          }
        }
      }
    }
    setInfo(BACKREFS,BACKREFS_SIZE,null);
    shrinkInfo(BACKREFS);
    }
   
    ///////////////////////////////////////////////////////////////////////////
   
    /**
     * This method will return a "staging" object associated with the proposed
     * modifications that are passed as argument. The purpose of staging objects
     * is to allow for the validation of a set of modifications before actually
     * applying them to the target object.
     * @param mods the modification that might be applied
     */
    public DmcObject getStagingObject(DmcTypeModifier mods){
      synchronized (attributes) {
        DmcObject rc = getNew();
        rc.staging(true);
       
        // The unique set of attributes affected by the modifcations
        HashSet<DmcAttributeInfo> mayBeChanged = new HashSet<DmcAttributeInfo>();
       
      Iterator<Modifier> modifiers = mods.getMV();
      while(modifiers.hasNext()){
        Modifier mod = modifiers.next();
        mayBeChanged.add(mod.getAttributeInfo());
      }
     
      for(DmcAttribute<?> attr: attributes.values()){
        if (mayBeChanged.contains(attr.getAttributeInfo())){
          // Store a clone of the affected attribute
          rc.attributes.put(attr.getID(), attr.cloneIt());
        }
        else{
          // Store a reference to the existing attribute
          rc.attributes.put(attr.getID(), attr);
        }
      }
     
        return(rc);
    }
    }
}
TOP

Related Classes of org.dmd.dmc.DmcObject

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.