Package nexj.core.runtime

Source Code of nexj.core.runtime.Instance

// Copyright 2010 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.runtime;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import nexj.core.meta.Accessor;
import nexj.core.meta.Attribute;
import nexj.core.meta.Event;
import nexj.core.meta.Member;
import nexj.core.meta.Metaclass;
import nexj.core.meta.Primitive;
import nexj.core.meta.Selector;
import nexj.core.meta.Type;
import nexj.core.meta.TypeConversionException;
import nexj.core.meta.Typed;
import nexj.core.meta.persistence.DataSource;
import nexj.core.meta.persistence.DataSourceFragment;
import nexj.core.meta.persistence.PersistenceMapping;
import nexj.core.persistence.AssociationIntegrityException;
import nexj.core.persistence.ConstraintViolationException;
import nexj.core.persistence.LazyLocation;
import nexj.core.persistence.OID;
import nexj.core.persistence.OIDHolder;
import nexj.core.persistence.OptimisticLockException;
import nexj.core.persistence.PersistenceAdapter;
import nexj.core.persistence.PersistenceException;
import nexj.core.persistence.PersistenceResolver;
import nexj.core.persistence.Query;
import nexj.core.scripting.Function;
import nexj.core.scripting.Machine;
import nexj.core.scripting.Pair;
import nexj.core.scripting.ScriptingException;
import nexj.core.scripting.Symbol;
import nexj.core.util.Binary;
import nexj.core.util.Invalid;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.Named;
import nexj.core.util.ObjUtil;
import nexj.core.util.PrintWriter;
import nexj.core.util.Printable;
import nexj.core.util.PropertyHashTab;
import nexj.core.util.PropertyIterator;
import nexj.core.util.PropertyMap;
import nexj.core.util.StackTrace;
import nexj.core.util.StringId;
import nexj.core.util.Undefined;

/**
* A generic persistent object instance.
* It is a closure and implements the Function interface.
* The first argument of that function is the method symbol.
*/
public final class Instance implements Accessor, InvocationContextHolder, ContextHolder, PropertyMap, OIDHolder, InstanceHolder, LazyLocation, Function, Typed, Named, Printable
{
   // constants

   /**
    * Initial state of the instance - not registered with the UOW.
    */
   public final static byte INIT = 0;

   /**
    * Clean instance - has an OID and is registered in the UOW instance table.
    */
   public final static byte CLEAN = 1;

   /**
    * New instance - has no OID and is registered in the UOW change table.
    */
   public final static byte NEW = 2;

   /**
    * Dirty instance - has an OID and is registered in the UOW instance and change tables.
    */
   public final static byte DIRTY = 3;

   /**
    * Deleted instance - has an OID and is registered in the UOW instance and change tables.
    */
   public final static byte DELETED = 4;

   /**
    * The state event is pending in the UOW.
    */
   private final static byte EVENT_PENDING = 0x01;

   /**
    * The state event has been already invoked once in the UOW.
    */
   private final static byte EVENT_INVOKED = 0x02;

   /**
    * The commit event has already been invoked once in the UOW.
    */
   private final static byte EVENT_COMMIT = 0x04;

   /**
    * The security flag when a pending event is flagged.
    */
   private final static byte EVENT_SECURE = 0x08;

   /**
    * Temporary flag for load attribute computation,
    * used to avoid allocating storage on each load invocation.
    */
   private final static byte LOAD_READ = 0x10;

   /**
    * Set if the instance has been restored from cache.
    */
   private final static byte CACHE_BUSY = 0x20;

   /**
    * Set if all the instance state is updateable.
    */
   private final static byte UPDATEABLE = 0x40;

   /**
    * Set if the instance visibility has been reduced.
    */
   private final static byte HIDDEN = (byte)0x80;

   /**
    * Maximum loading depth for circular dependency detection.
    */
   private final static int MAX_LOADING_DEPTH = 1000;

   /**
    * State names, indexed by state number.
    */
   private final static String[] STATE_NAME_ARRAY = new String[]{"INIT", "CLEAN", "NEW", "DIRTY", "DELETED"};

   // attributes

   /**
    * The instance state (INIT, CLEAN, NEW, DIRTY, DELETED).
    */
   private byte m_nState;

   /**
    * Combination of pending system event (EVENT_*) and other flags.
    */
   private byte m_nFlags;

   /**
    * The loading depth counter.
    */
   private short m_nLoadingDepth;

   // associations

   /**
    * The object state. The values are indexed by the attribute ordinals.
    */
   private Object[] m_valueArray;

   /**
    * The object state before updating - a copy of m_valueArray.
    */
   private Object[] m_oldValueArray;

   /**
    * The object state before updating since the last life cycle event -
    * a copy of m_valueArray.
    */
   private Object[] m_preValueArray;

   /**
    * The annotation name to value map: Object[String].
    */
   private PropertyHashTab m_annotationMap;

   /**
    * This is the instance class.
    */
   private Metaclass m_metaclass;

   /**
    * The object OID.
    */
   private OID m_oid;

   /**
    * The object invocation context.
    */
   private InvocationContext m_context;

   /**
    * The object's current unit of work. Can be null.
    */
   private UnitOfWork m_uow;

   /**
    * Bit set for overridden calculated values.
    * The bit offset corresponds to the attribute ordinal number.
    */
   private byte[] m_overrideArray;

   /**
    * The class logger.
    */
   private final static Logger s_logger = Logger.getLogger(Instance.class);

   // constructors

   /**
    * Creates an instance of a given class with a specified state.
    * @param metaclass The metaclass of which to create an instance.
    * @param nState One of the Instance.* state constants.
    * @param context The invocation context.
    */
   public Instance(Metaclass metaclass, byte nState, InvocationContext context)
   {
      this(metaclass, false, context);
      setState(nState);
   }

   /**
    * Creates an undefined instance of a given class.
    * The instance state has to be set separately.
    * @param metaclass The metaclass of which to create an instance.
    * @param context The invocation context.
    */
   public Instance(Metaclass metaclass, InvocationContext context)
   {
      this(metaclass, false, context);
   }

   /**
    * Creates an undefined instance of a given class.
    * The instance state has to be set separately.
    * @param metaclass The metaclass of which to create an instance.
    * @param bLazy True to construct a lazy-loaded instance with deferred class identification.
    * @param context The invocation context.
    */
   public Instance(Metaclass metaclass, boolean bLazy, InvocationContext context)
   {
      assert metaclass != null;

      m_metaclass = metaclass;
      m_context = context;

      if (!bLazy)
      {
         m_valueArray = new Object[metaclass.getInstanceAttributeCount()];
         Arrays.fill(m_valueArray, Undefined.VALUE);
      }
   }

   // operations

   /**
    * Sets the metaclass of a lazy-loaded instance.
    */
   public void setMetaclass(Metaclass metaclass)
   {
      assert isLazy();

      m_metaclass = metaclass;
      m_valueArray = new Object[metaclass.getInstanceAttributeCount()];
      Arrays.fill(m_valueArray, Undefined.VALUE);
   }

   /**
    * @return The instance class.
    * @see nexj.core.meta.Accessor#getMetaclass()
    */
   public Metaclass getMetaclass()
   {
      load();

      return m_metaclass;
   }

   /**
    * Sets the tentative class of a lazy-loaded instance.
    * The instance remains lazy-loaded.
    */
   public void setLazyMetaclass(Metaclass metaclass)
   {
      assert isLazy();

      m_metaclass = metaclass;
   }

   /**
    * @see nexj.core.meta.Accessor#getLazyMetaclass()
    */
   public Metaclass getLazyMetaclass()
   {
      return m_metaclass;
   }

   /**
    * @see nexj.core.meta.Accessor#isLazy()
    */
   public boolean isLazy()
   {
      return m_valueArray == null;
   }

   /**
    * @return True if the instance is missing.
    */
   public boolean isMissing()
   {
      if (isLazy())
      {
         try
         {
            load((Pair)null, false);
         }
         catch (AssociationIntegrityException ex)
         {
            return true;
         }
      }

      return false;
   }

   /**
    * @see nexj.core.runtime.InstanceHolder#getInstance()
    */
   public Instance getInstance()
   {
      return this;
   }

   /**
    * @see nexj.core.meta.Typed#getType()
    */
   public Type getType()
   {
      return getMetaclass();
   }

   /**
    * @see nexj.core.meta.OIDHolder#setOID(nexj.core.meta.OID)
    */
   public void setOID(OID oid)
   {
      if (m_oid != null)
      {
         if (m_oid.equals(oid))
         {
            m_oid = oid;
            return;
         }

         m_context.removeInstance(this);

         if (m_uow != null && oid != null)
         {
            m_uow.changeOID(this);
         }
      }

      m_oid = oid;

      if (oid != null)
      {
         m_context.addInstance(this);
      }
   }

   /**
    * @see nexj.core.meta.OIDHolder#getOID()
    */
   public OID getOID()
   {
      return m_oid;
   }

   /**
    * @see nexj.core.persistence.LazyLocation#getLazyClassName()
    */
   public String getLazyClassName()
   {
      return m_metaclass.getName();
   }

   /**
    * @see nexj.core.persistence.LazyLocation#getLazyCaption()
    */
   public String getLazyCaption()
   {
      return m_metaclass.getCaption();
   }

   /**
    * Caches an instance in initial state in the invocation context and transitions it to clean state.
    * @param oid The instance OID.
    * @return The instance reference.
    */
   public InstanceRef cache(OID oid)
   {
      assert m_nState == INIT;
      assert m_uow == null;
      assert m_oid == null;
      assert (m_nFlags & EVENT_PENDING) == 0;

      m_oid = oid;

      InstanceRef ref = m_context.addInstance(this);

      m_nState = CLEAN;

      return ref;
   }

   /**
    * @return The Invocation context.
    */
   public InvocationContext getInvocationContext()
   {
      return m_context;
   }

   /**
    * @see nexj.core.runtime.ContextHolder#getContext()
    */
   public Context getContext()
   {
      return m_context;
   }

   /**
    * Sets the current unit of work.
    * @param uow The current unit of work to set.
    */
   public void setUnitOfWork(UnitOfWork uow)
   {
      m_uow = uow;
   }

   /**
    * @return The current unit of work.
    */
   public UnitOfWork getUnitOfWork()
   {
      return m_uow;
   }

   /**
    * @return The persistence mapping for this instance. Can be null.
    */
   public PersistenceMapping getPersistenceMapping()
   {
      PersistenceMapping mapping = m_metaclass.getPersistenceMapping();

      if (mapping != null && mapping.isDynamic())
      {
         mapping = ((PersistenceResolver)mapping.getDataSource().getComponent().getInstance(m_context)).getMapping(this);
      }

      return mapping;
   }

   /**
    * @return The data source fragment name of the instance. Can be null.
    */
   public String getFragmentName()
   {
      PersistenceMapping mapping = m_metaclass.getPersistenceMapping();
      String sName = null;

      if (mapping != null && mapping.isDynamic())
      {
         sName = ((PersistenceResolver)mapping.getDataSource().getComponent().getInstance(m_context)).getFragmentName(this);
      }

      if (mapping == null)
      {
         return null;
      }

      if (sName == null)
      {
         sName = m_context.getUnitOfWork().getFragmentName(mapping.getDataSource().getFragmentCount() != 1);
      }

      if (sName != null && sName.length() == 0)
      {
         sName = null;
      }

      return sName;
   }

   /**
    * @return The persistence adapter for this instance.
    */
   public PersistenceAdapter getAdapter()
   {
      return (PersistenceAdapter)getPersistenceMapping()
         .getDataSource().getComponent().getInstance(m_context);
   }

   /**
    * Clones the values into previous values.
    */
   private Object[] cloneValues()
   {
      Object[] valueArray = (Object[])m_valueArray.clone();

      for (int i = 0, n = m_metaclass.getInstanceAttributeCount(); i < n; ++i)
      {
         if (m_metaclass.getInstanceAttribute(i).isCalculated())
         {
            valueArray[i] = Undefined.VALUE;
         }
      }

      return valueArray;
   }

   /**
    * Commits the instance.
    */
   public void commit()
   {
      assert m_oid != null || getPersistenceMapping() == null || m_nState == DELETED;

      switch (m_nState)
      {
         case INIT:
            m_nState = CLEAN;

            break;

         case NEW:
         case DIRTY:
            m_uow.removeChange(this);
            m_nState = CLEAN;
            m_oldValueArray = null;
            m_preValueArray = null;
            m_overrideArray = null;

            break;

         case DELETED:
            m_uow.removeChange(this);
            m_context.removeInstance(this);

            if (m_preValueArray != null)
            {
               System.arraycopy(m_valueArray, 0, m_preValueArray, 0, m_valueArray.length);
            }

            break;
      }

      m_nFlags &= HIDDEN;
      m_uow = null;
   }

   /**
    * Rolls the instance back to CLEAN state.
    */
   public void rollback()
   {
      switch (m_nState)
      {
         case INIT:
         case NEW:
            m_nState = DELETED;
            setOID(null);

            break;

         case DIRTY:
            m_nState = CLEAN;
            m_valueArray = m_oldValueArray;
            m_oldValueArray = null;
            m_overrideArray = null;

            break;

         case DELETED:
            m_nState = CLEAN;

            if (m_oldValueArray != null)
            {
               m_valueArray = m_oldValueArray;
               m_oldValueArray = null;
               m_overrideArray = null;
            }

            break;
      }

      m_preValueArray = null;
      m_nFlags &= HIDDEN;
      m_uow = null;
   }

   /**
    * Transitions the instance to CLEAN state.
    */
   public void setClean()
   {
      assert m_oid != null || getPersistenceMapping() == null;

      switch (m_nState)
      {
         case INIT:
            m_nState = CLEAN;

            break;

         case NEW:
            m_uow.removeChange(this);
            m_nState = CLEAN;

            break;

         case DIRTY:
         case DELETED:
            m_uow.removeChange(this);
            m_nState = CLEAN;
            m_oldValueArray = null;
            m_overrideArray = null;

            break;
      }

      m_preValueArray = null;
      m_nFlags &= HIDDEN;
      m_uow = null;
   }

   /**
    * Transitions the instance to NEW state.
    */
   public void setNew()
   {
      assert !isLazy();

      switch (m_nState)
      {
         case INIT:
            assert m_oid == null;

            m_context.manageTransaction(m_metaclass.getCreateTransactionMode());
            m_nState = NEW;
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

            break;

         case CLEAN:
            assert m_oid != null;

            m_context.removeInstance(this);
            m_context.manageTransaction(m_metaclass.getCreateTransactionMode());
            m_nState = NEW;
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

            break;

         case NEW:
            return;

         default:
            throw new IllegalStateException("Cannot create an already initialized instance");
      }

      m_preValueArray = cloneValues();
   }

   /**
    * Transitions the instance to DIRTY state.
    */
   public void setDirty()
   {
      load();

      switch (m_nState)
      {
         case INIT:
            assert m_oid != null;

            m_context.manageTransaction(m_metaclass.getUpdateTransactionMode());
            m_oldValueArray = (Object[])m_valueArray.clone();
            m_preValueArray = cloneValues();
            m_context.addInstance(this);
            m_nState = DIRTY;
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

            break;

         case CLEAN:
            assert m_oid != null || m_metaclass.getPersistenceMapping() == null;

            m_context.manageTransaction(m_metaclass.getUpdateTransactionMode());
            m_oldValueArray = (Object[])m_valueArray.clone();
            m_preValueArray = cloneValues();
            m_nState = DIRTY;
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

            break;

         case NEW:
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

         case DIRTY:
            break;

         default:
            throw new IllegalStateException("Cannot modify an already deleted instance");
      }
   }

   /**
    * Transitions the instance to REMOVED state.
    */
   public void setDeleted()
   {
      load();

      switch (m_nState)
      {
         case INIT:
            assert getPersistenceMapping() == null || m_oid != null;

            m_context.addInstance(this);
            m_nState = DELETED;
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

            break;

         case CLEAN:
            assert m_oid != null;

            m_nState = DELETED;
            setEventPending(true);
            m_context.getUnitOfWork().addChange(this);

            break;

         case NEW:
            m_nState = DELETED;
            setEventPending(true);
            m_uow.removeChange(this);

            break;

         case DIRTY:
            setEventPending(true);
            m_uow.addChange(this);

         case DELETED:
            m_nState = DELETED;
            m_uow.accumulateChange(this);

            break;
      }

      if (m_preValueArray != null)
      {
         System.arraycopy(m_valueArray, 0, m_preValueArray, 0, m_valueArray.length);
      }
   }

   /**
    * Transitions instance state (CLEAN, NEW, DIRTY, DELETED).
    * The state after this method invocation is not necessarily nState.
    * @param nState The instance state to set.
    */
   public void setState(byte nState)
   {
      switch (nState)
      {
         case INIT:
            if (m_nState != INIT)
            {
               throw new IllegalStateException("Cannot go back to INIT state");
            }

            break;

         case CLEAN:
            setClean();

            break;

         case NEW:
            setNew();

            break;

         case DIRTY:
            setDirty();

            break;

         case DELETED:
            setDeleted();

            break;

         default:
            throw new IllegalArgumentException("Invalid state: " + nState);
      }
   }

   /**
    * @return The instance state (INIT, CLEAN, NEW, DIRTY, DELETED).
    */
   public byte getState()
   {
      return m_nState;
   }

   /**
    * Moves the instance to the back of the change queue.
    */
   public void requeue()
   {
      if (m_nState > CLEAN)
      {
         m_context.getUnitOfWork().addChange(this);
      }
   }

   /**
    * Suspends the pending system event.
    * Not the same as setEventpending(false), as it does not change any internal state.
    */
   public void suspendEvent()
   {
      m_nFlags &= ~EVENT_PENDING;
   }

   /**
    * Sets the system event pending flag.
    * @param bEventPending The system event pending flag to set.
    */
   public void setEventPending(boolean bEventPending)
   {
      if (bEventPending)
      {
         if ((m_nFlags & EVENT_PENDING) != 0)
         {
            if (m_context.isSecure())
            {
               m_nFlags |= EVENT_SECURE;
            }
         }
         else
         {
            m_nFlags |= EVENT_PENDING;

            if (m_context.isSecure())
            {
               m_nFlags |= EVENT_SECURE;
            }
            else
            {
               m_nFlags &= ~EVENT_SECURE;
            }
         }
      }
      else
      {
         suspendEvent();

         if (m_preValueArray != null)
         {
            int nValueCount = m_valueArray.length;

            // Minimize the work
            if (nValueCount * 3 < m_uow.getCollectionCount())
            {
               for (int i = 0; i < nValueCount; ++i)
               {
                  Object value = m_valueArray[i];

                  if (value instanceof InstanceList)
                  {
                     ((InstanceList)value).finish();
                  }

                  Object pre = m_preValueArray[i];

                  if (pre != value && pre instanceof InstanceList)
                  {
                     ((InstanceList)pre).finish();
                  }

                  if (m_oldValueArray != null)
                  {
                     Object old = m_oldValueArray[i];

                     if (old != pre && old != value && old instanceof InstanceList)
                     {
                        ((InstanceList)old).finish();
                     }
                  }
               }
            }
            else
            {
               for (Iterator itr = m_uow.getCollectionIterator(); itr.hasNext();)
               {
                  InstanceList list = (InstanceList)itr.next();

                  if (list.getContainer() == this)
                  {
                     list.finish();
                  }
               }
            }

            System.arraycopy(m_valueArray, 0, m_preValueArray, 0, m_valueArray.length);
         }
      }
   }

   /**
    * @return The system event pending flag.
    */
   public boolean isEventPending()
   {
      return (m_nFlags & EVENT_PENDING) != 0;
   }

   /**
    * Sets the system event invocation flag and clears the pending event flag if the invocation flag is true.
    * @param bEventInvoked The system event invocation flag to set.
    */
   public void setEventInvoked(boolean bEventInvoked)
   {
      if (bEventInvoked)
      {
         setEventPending(false);
         m_nFlags |= EVENT_INVOKED;
      }
      else
      {
         m_nFlags &= ~EVENT_INVOKED;
      }
   }

   /**
    * @return The system event invocation flag.
    */
   public boolean isEventInvoked()
   {
      return (m_nFlags & EVENT_INVOKED) != 0;
   }

   /**
    * @return The pending event name, null if none.
    */
   public String getPendingEventName()
   {
      if ((m_nFlags & EVENT_PENDING) != 0)
      {
         switch (m_nState)
         {
            case NEW:
               if ((m_nFlags & EVENT_INVOKED) != 0)
               {
                  return "update";
               }

               return "create";

            case DIRTY:
               return "update";

            case DELETED:
               return "delete";
         }
      }

      return null;
   }

   /**
    * Invokes a system event.
    * @param sEvent The system event name. Can be null.
    */
   protected void invokeSystemEvent(String sEvent)
   {
      if (sEvent != null)
      {
         boolean bSecure = (m_nFlags & EVENT_SECURE) != 0;

         if (bSecure == m_context.isSecure())
         {
            invoke(sEvent);
         }
         else
         {
            boolean bSecureSaved = m_context.isSecure();

            try
            {
               m_context.setSecure(bSecure);
               invoke(sEvent);
            }
            finally
            {
               m_context.setSecure(bSecureSaved);
            }
         }
      }
   }

   /**
    * Invokes the pending event.
    */
   public void invokePendingEvent()
   {
      invokeSystemEvent(getPendingEventName());
   }

   /**
    * Invokes a suspended system event.
    * @return True if the event has been invoked.
    */
   public boolean invokeSuspendedEvent()
   {
      if ((m_nFlags & EVENT_PENDING) != 0)
      {
         invokePendingEvent();

         return true;
      }

      if ((m_nFlags & EVENT_INVOKED) == 0)
      {
         switch (m_nState)
         {
            case NEW:
               invokeSystemEvent("create");

               return true;

            case DIRTY:
               invokeSystemEvent("update");

               return true;
         }
      }

      return false;
   }

   /**
    * Sets the commit pending flag.
    * @param bCommitPending The commit pending flag to set.
    */
   public void setCommitPending(boolean bCommitPending)
   {
      if (bCommitPending)
      {
         m_nFlags &= ~EVENT_COMMIT;
      }
      else
      {
         m_nFlags |= EVENT_COMMIT;
      }
   }

   /**
    * @return True if a commit event should be invoked for the instance.
    */
   public boolean isCommitPending()
   {
      return (m_nFlags & EVENT_COMMIT) == 0 && (m_nState == NEW || m_nState == DIRTY);
   }

   /**
    * Marks the instance as cached.
    */
   public void setCached(boolean bCached)
   {
      if (bCached)
      {
         if (!isLazy())
         {
            m_nFlags |= CACHE_BUSY;
         }
      }
      else
      {
         m_nFlags &= ~CACHE_BUSY;
      }
   }

   /**
    * @return True if the instance is marked as cached.
    */
   public boolean isCached()
   {
      return (m_nFlags & CACHE_BUSY) != 0;
   }

   /**
    * @return True if the visibility has been reduced.
    */
   public boolean isHidden()
   {
      return (m_nFlags & HIDDEN) != 0;
   }

   /**
    * Computes the hiddenness flag.
    */
   public void computeHiddenness()
   {
      if ((m_nFlags & HIDDEN) == 0)
      {
         switch (m_nState)
         {
            case NEW:
               m_nFlags |= HIDDEN;
               break;

            case DIRTY:
            case DELETED:
               if (m_context.getGeneration() != InvocationContext.GEN_NEW)
               {
                  break;
               }

               Attribute attribute = m_metaclass.getReadAccessAttribute();

               if (m_nState == DIRTY && attribute != null && !attribute.isStatic() &&
                  getValueDirect(attribute.getOrdinal()) == Undefined.VALUE)
               {
                  break;
               }

               if (isReadable())
               {
                  if (m_nState == DELETED)
                  {
                     m_nFlags |= HIDDEN;
                  }
               }
               else
               {
                  try
                  {
                     m_context.setGeneration(InvocationContext.GEN_OLD);

                     if (isReadable())
                     {
                        m_nFlags |= HIDDEN;
                     }
                  }
                  finally
                  {
                     m_context.setGeneration(InvocationContext.GEN_NEW);
                  }
               }

               break;
         }
      }
   }

   /**
    * Sets the loading flag.
    * @param bLoading The loading flag to set.
    */
   public void setLoading(boolean bLoading)
   {
      if (bLoading)
      {
         ++m_nLoadingDepth;
      }
      else
      {
         --m_nLoadingDepth;
      }
   }

   /**
    * @return The loading flag.
    */
   public boolean isLoading()
   {
      return m_nLoadingDepth != 0;
   }

   /**
    * Invokes the load event for the specified attribute.
    * @param symbol The attribute symbol.
    */
   public void load(Symbol symbol)
   {
      ((Event)m_metaclass.getSelector("load").getMember(1)).invoke(
         new Object[]{this, symbol}, m_context.getMachine());
   }

   /**
    * Discards the value of a given attribute.
    * @param attribute The attribute which value to discard.
    * @param bReverseCollection True to remove an item from a reverse collection.
    */
   public void discard(Attribute attribute, boolean bReverseCollection)
   {
      assert m_uow == null;

      if (!isLazy())
      {
         attribute = m_metaclass.getInstanceAttribute(attribute.getOrdinal());

         Attribute reverse = attribute.getReverse();
         byte nGenerationSaved = m_context.getGeneration();

         try
         {
            for (byte nGeneration = nGenerationSaved;;)
            {
               Object oldValue = getValueDirect(attribute.getOrdinal());

               if (!(oldValue instanceof Undefined))
               {
                  attribute.invalidateDependency(this, Undefined.VALUE);

                  if (oldValue != null)
                  {
                     if (attribute.isCollection())
                     {
                        InstanceList list = (InstanceList)oldValue;

                        list.setLazy(true);

                        for (int i = list.getCount() - 1; i >= 0; --i)
                        {
                           Instance instance = list.getInstance(i);

                           if (instance == null)
                           {
                              list.remove(i, InstanceList.DIRECT);
                           }
                           else if (instance.getUnitOfWork() == null && !instance.isLazy())
                           {
                              if (reverse != null)
                              {
                                 if (!(instance.getValueDirect(reverse.getOrdinal()) instanceof Undefined))
                                 {
                                    instance.setValueDirect(reverse.getOrdinal(), Undefined.VALUE);
                                    instance.getMetaclass().getInstanceAttribute(reverse.getOrdinal())
                                       .invalidateDependency(instance, Undefined.VALUE);
                                 }
                              }

                              list.remove(i, InstanceList.DIRECT);
                           }
                        }
                     }
                     else
                     {
                        if (reverse != null)
                        {
                           Instance instance = (Instance)oldValue;

                           if (instance.getUnitOfWork() == null && !instance.isLazy())
                           {
                              if (reverse.isCollection())
                              {
                                 if (bReverseCollection)
                                 {
                                    Object reverseValue = instance.getValueDirect(reverse.getOrdinal());

                                    if (reverseValue != Undefined.VALUE && reverseValue != null)
                                    {
                                       InstanceList list = (InstanceList)reverseValue;

                                       list.setLazy(true);

                                       if (list.remove(this, InstanceList.DIRECT))
                                       {
                                          instance.getMetaclass().getInstanceAttribute(reverse.getOrdinal())
                                             .invalidateDependency(instance, Undefined.VALUE);
                                       }
                                    }
                                 }
                              }
                              else
                              {
                                 instance.setValueDirect(reverse.getOrdinal(), Undefined.VALUE);
                              }
                           }
                        }

                        setValueDirect(attribute.getOrdinal(), Undefined.VALUE);
                     }
                  }
                  else
                  {
                     setValueDirect(attribute.getOrdinal(), Undefined.VALUE);
                  }
               }

               if (++nGeneration > InvocationContext.GEN_OLD)
               {
                  break;
               }

               m_context.setGeneration(nGeneration);
            }
         }
         finally
         {
            m_context.setGeneration(nGenerationSaved);
         }
      }
   }

   /**
    * Sets/add an association.
    * @param nOrdinal The ordinal number of the association to set.
    * @param instance The instance to add.
    * @param bDirect True to disable side effects.
    */
   public void associate(int nOrdinal, Instance instance, boolean bDirect)
   {
      load();

      Attribute attribute = m_metaclass.getInstanceAttribute(nOrdinal);

      if (attribute.isCollection())
      {
         Object value = getValueDirect(nOrdinal);
         InstanceList list;

         if (value instanceof Undefined)
         {
            list = new InstanceArrayList();

            setValueDirect(nOrdinal, list);
            list.setAssociation(this, attribute, true);
         }
         else
         {
            list = (InstanceList)value;
         }

         if (list.add(instance, InstanceList.REPLACE | InstanceList.DIRECT | InstanceList.WEAK | InstanceList.TRACK))
         {
            if (!bDirect)
            {
               attribute.invalidateDependency(this, Invalid.VALUE);
            }
         }
      }
      else if (bDirect)
      {
         Object value = getValueDirect(nOrdinal);

         if (value instanceof Undefined)
         {
            setValueDirect(nOrdinal, instance);
         }
      }
      else
      {
         assign(attribute, instance, false);
      }
   }

   /**
    * Removes an association.
    * @param nOrdinal The ordinal number of the association to remove.
    * @param instance The instance to remove.
    * @param bDirect True to disable side effects.
    */
   public void dissociate(int nOrdinal, Instance instance, boolean bDirect)
   {
      load();

      Attribute attribute = m_metaclass.getInstanceAttribute(nOrdinal);

      if (attribute.isCollection())
      {
         Object value;

         if (bDirect)
         {
            value = getValueDirect(nOrdinal);
         }
         else
         {
            value = getValue(nOrdinal);
            attribute.invalidateDependency(this, Invalid.VALUE);
         }

         if (!(value instanceof Undefined))
         {
            InstanceList list = (InstanceList)value;

            list.remove(instance, (bDirect) ? InstanceList.DIRECT : InstanceList.DIRECT | InstanceList.TRACK);
         }
      }
      else
      {
         if (bDirect)
         {
            setValueDirect(nOrdinal, null);
         }
         else
         {
            attribute.invalidateDependency(this, Invalid.VALUE);
            assign(attribute, null, false);
         }
      }
   }

   /**
    * @return True if the instance is readable to the current user.
    */
   public boolean isReadable()
   {
      load();

      if (!m_metaclass.isReadable(m_context.getPrivilegeSet()))
      {
         return false;
      }

      Attribute attribute = m_metaclass.getReadAccessAttribute();

      if (attribute != null)
      {
         if (m_context.isSecure())
         {
            Object value = (attribute.isStatic()) ?
               m_metaclass.getValue(attribute.getOrdinal()) :
                  getValue(attribute.getOrdinal());

            if (!Boolean.TRUE.equals(value))
            {
               return false;
            }
         }
      }

      return true;
   }

   /**
    * Checks if a given attribute is readable.
    * @param attribute The attribute to check.
    * @return True if readable.
    */
   public boolean isReadable(Attribute attribute)
   {
      load();

      return attribute.isReadable(m_context.getPrivilegeSet());
   }

   /**
    * Checks if a given attribute is updateable.
    * @param attribute The attribute to check.
    * @return True if updateable.
    */
   public boolean isUpdateable(Attribute attribute)
   {
      if ((m_nFlags & UPDATEABLE) != 0)
      {
         return true;
      }

      load();

      if (!m_context.isSecure())
      {
         return !attribute.isReadOnly();
      }

      if (!attribute.isUpdateable(m_context.getPrivilegeSet()))
      {
         return false;
      }

      if (m_nState != NEW)
      {
         Attribute accessAttribute = attribute.getAccessAttribute();

         if (accessAttribute != null)
         {
            int nOrdinal = accessAttribute.getOrdinal();

            if (accessAttribute.isStatic())
            {
               return !Boolean.FALSE.equals(m_metaclass.getValue(nOrdinal));
            }

            return !Boolean.FALSE.equals(getValue(nOrdinal)) ||
               !Boolean.FALSE.equals(getOldValue(nOrdinal));
         }
      }

      return true;
   }

   /**
    * Checks the access rights to a given attribute.
    * @param attribute The attribute to check.
    * @throws SecurityViolationException if the access is denied.
    */
   public void checkUpdateAccess(Attribute attribute) throws SecurityViolationException
   {
      if (!isUpdateable(attribute))
      {
         throw new SecurityViolationException(
            (attribute.isReadOnly()) ?
               "err.runtime.attributeReadOnlyAccess" :
               "err.runtime.attributeUpdateAccess",
            new Object[]{attribute.getName(), m_metaclass.getName()});
      }
   }

   /**
    * Checks the access rights to a given attribute.
    * @param nOrdinal The attribute ordinal number.
    * @throws SecurityViolationException if the access is denied.
    */
   public void checkUpdateAccess(int nOrdinal) throws SecurityViolationException
   {
      load();

      checkUpdateAccess(m_metaclass.getInstanceAttribute(nOrdinal));
   }

   /**
    * Checks the access rights to a given attribute.
    * @param nOrdinal The attribute ordinal number.
    * @param instance The instance which is going to be assigned.
    * @throws SecurityViolationException if the access is denied.
    */
   public void checkUpdateAccess(int nOrdinal, Instance instance) throws SecurityViolationException
   {
      load();

      Attribute attribute = m_metaclass.getInstanceAttribute(nOrdinal);

      if (attribute.isCollection())
      {
         Object value = getValueDirect(nOrdinal);

         if (value instanceof InstanceList)
         {
            InstanceList list = (InstanceList)value;

            if (!list.isLazy() && list.contains(instance))
            {
               return;
            }
         }
      }
      else
      {
         if (getValueDirect(nOrdinal) == instance)
         {
            return;
         }
      }

      if (!isUpdateable(attribute))
      {
         if (attribute.isCollection())
         {
            InstanceList list = (InstanceList)getValue(nOrdinal);

            if (list == null || !list.contains(instance))
            {
               checkUpdateAccess(attribute);
            }
         }
         else
         {
            if (getValue(nOrdinal) != instance)
            {
               checkUpdateAccess(attribute);
            }
         }
      }
   }

   /**
    * Initializes the aliased associations for a newly assigned collection.
    * @param list The newly assigned collection.
    */
   private void setAliases(InstanceList list)
   {
      assert !isLazy();

      Attribute attribute = list.getAttribute();
      Attribute alias = attribute.getAlias();

      if (alias != null)
      {
         while (alias.getOrdinal() != attribute.getOrdinal())
         {
            Object value = getValueDirect(alias.getOrdinal());

            if (value != null && !(value instanceof Undefined))
            {
               if (value instanceof InstanceList)
               {
                  InstanceList aliasList = (InstanceList)value;

                  if (aliasList.getCount() != 0)
                  {
                     list.setLoading();
                  }

                  if (list.getCount() != 0)
                  {
                     aliasList.setLoading();
                  }
               }
               else
               {
                  list.setLoading();
               }
            }

            alias = alias.getAlias();
         }
      }
   }

   /**
    * Updates the aliased associations of a given attribute.
    * @param attribute The attribute for which to update the associations.
    */
   protected void updateAliases(Attribute attribute)
   {
      assert !isLazy();

      Attribute alias = attribute.getAlias();

      if (alias != null)
      {
         while (alias.getOrdinal() != attribute.getOrdinal())
         {
            Object value = getValueDirect(alias.getOrdinal());

            if (value != null && !(value instanceof Undefined))
            {
               if (value instanceof InstanceList)
               {
                  ((InstanceList)value).setLoading();
               }
            }

            alias = alias.getAlias();
         }
      }
   }

   /**
    * Assigns a value without side effects,
    * except for instance state management.
    * This method is for INTERNAL USE ONLY.
    * @param attribute The attribute object.
    * @param value The attribute value.
    * @param bAssoc True to set the reverse assoc.
    */
   public void assign(Attribute attribute, Object value, boolean bAssoc)
   {
      assert !isLazy();

      int nOrdinal = attribute.getOrdinal();
      Object oldValue = getValueDirect(nOrdinal);

      if (!ObjUtil.equal(oldValue, value))
      {
         if (!isLoading())
         {
            if (!attribute.isCollection() &&
               (m_metaclass.getPersistenceMapping() == null ||
                  m_metaclass.getPersistenceMapping().getLockingAttribute() != attribute))
            {
               if (m_nState == DIRTY || m_nState == NEW)
               {
                  if (m_uow != m_context.getUnitOfWork() && m_context.isUnitOfWorkGlobal())
                  {
                     throw new TransactionException("err.runtime.wrongTransaction",
                        new Object[]{getLazyClassName()});
                  }

                  setEventPending(true);
               }
               else
               {
                  setDirty();
               }
            }

            if (attribute.isCalculated())
            {
               setOverridden(nOrdinal);
            }
         }

         if (attribute.isCollection())
         {
            InstanceList list = (InstanceList)value;

            if (list != null && attribute.getReverse() != null)
            {
               list.checkUpdateAccess(attribute.getReverse(), this);
            }

            setValueDirect(nOrdinal, list);

            if (list != null)
            {
               list.setAssociation(this, attribute, false);
               setAliases(list);
            }
         }
         else if (bAssoc)
         {
            Attribute reverse = attribute.getReverse();

            if (reverse != null)
            {
               int nRevOrdinal = reverse.getOrdinal();

               if (oldValue instanceof Instance && attribute.isReverseOf(reverse))
               {
                  Instance instance = (Instance)oldValue;

                  instance.checkUpdateAccess(nRevOrdinal);
                  instance.dissociate(nRevOrdinal, this, false);
               }

               Instance instance = (Instance)value;

               if (instance != null)
               {
                  instance.checkUpdateAccess(nRevOrdinal, this);
               }

               setValueDirect(nOrdinal, instance);

               if (instance != null)
               {
                  instance.associate(nRevOrdinal, this, false);
               }
            }
            else
            {
               setValueDirect(nOrdinal, value);
            }
         }
         else
         {
            setValueDirect(nOrdinal, value);
         }
      }
   }

   /**
    * @see nexj.core.meta.Accessor#setValue(int, java.lang.Object)
    */
   public void setValue(int nOrdinal, Object value)
   {
      assert !isLazy();

      Attribute attribute = m_metaclass.getInstanceAttribute(nOrdinal);

      value = convert(attribute, value);

      Object oldValue = getValueDirect(nOrdinal);

      if (!ObjUtil.equal(oldValue, value))
      {
         if (!isLoading())
         {
            checkUpdateAccess(attribute);

            if (oldValue == Undefined.VALUE && attribute.isReverseInverseDependencyPersistent())
            {
               oldValue = getValue(nOrdinal);
            }

            if (m_nState == DIRTY || m_nState == NEW)
            {
               if (m_uow != m_context.getUnitOfWork() && m_context.isUnitOfWorkGlobal())
               {
                  throw new TransactionException("err.runtime.wrongTransaction",
                     new Object[]{getLazyClassName()});
               }

               if (!isEventPending())
               {
                  m_context.getUnitOfWork().addChange(this);
               }

               setEventPending(true);
            }
            else
            {
               setDirty();
            }

            if (attribute.isCalculated())
            {
               setOverridden(nOrdinal);
            }

            attribute.invalidateDependency(this, Invalid.VALUE);
         }

         if (attribute.isCollection())
         {
            if (attribute.getType().isPrimitive())
            {
               setValueDirect(nOrdinal, (List)value);
            }
            else
            {
               InstanceList list = (InstanceList)value;

               if (list != null && !isLoading())
               {
                  list.checkUpdateAccess(attribute.getReverse(), this);
               }

               setValueDirect(nOrdinal, list);

               if (list != null)
               {
                  list.setAssociation(this, attribute, isLoading());
                  setAliases(list);
               }
            }
         }
         else
         {
            Attribute reverse = attribute.getReverse();

            if (reverse != null)
            {
               int nRevOrdinal = reverse.getOrdinal();

               if (oldValue instanceof Instance && attribute.isReverseOf(reverse))
               {
                  Instance instance = (Instance)oldValue;

                  if (!instance.isMissing())
                  {
                     if (!isLoading())
                     {
                        instance.checkUpdateAccess(nRevOrdinal);
                     }

                     instance.dissociate(nRevOrdinal, this, isLoading());
                  }
               }

               Instance instance = (Instance)value;

               if (instance != null && !isLoading())
               {
                  instance.checkUpdateAccess(nRevOrdinal, this);
               }

               setValueDirect(nOrdinal, instance);

               if (instance != null)
               {
                  instance.associate(nRevOrdinal, this, isLoading());
               }
            }
            else
            {
               setValueDirect(nOrdinal, value);
            }
         }
      }
   }

   /**
    * @see nexj.core.meta.Accessor#setValue(java.lang.String, java.lang.Object)
    */
   public void setValue(String sName, Object value)
   {
      load();

      Attribute attribute = m_metaclass.getAttribute(sName);

      if (attribute.isStatic())
      {
         m_metaclass.setValue(attribute.getOrdinal(), value);
      }
      else
      {
         setValue(attribute.getOrdinal(), value);
      }
   }

   /**
    * @see nexj.core.meta.Accessor#getValue(int)
    */
   public Object getValue(int nOrdinal)
   {
      assert !isLazy();

      Object value = getValueDirect(nOrdinal);
      Attribute attribute = m_metaclass.getInstanceAttribute(nOrdinal);

      if (!(value instanceof Undefined) && attribute.isCached() &&
         (!attribute.isCalculated() || attribute.isPersistent() ||
            m_context.getGeneration() == InvocationContext.GEN_NEW || m_nState >= NEW))
      {
         return value;
      }

      ++m_nLoadingDepth;

      try
      {
         if (m_nLoadingDepth > MAX_LOADING_DEPTH)
         {
            throw new IterationCountException("err.persistence.circularLoading",
               new Object[]{attribute.getName(), m_metaclass.getName()});
         }

         if (m_nState == NEW && value == Undefined.VALUE && attribute.getInitializer() != Undefined.VALUE)
         {
            return setCalcValue(attribute,
               m_context.getMachine().invoke(attribute.getInitializerFunction(), new Pair(this)));
         }

         if (attribute.getValueFunction() != null)
         {
            return setCalcValue(attribute,
               m_context.getMachine().invoke(attribute.getValueFunction(), new Pair(this)));
         }

         if (attribute.isCollection() && attribute.isPersistent() &&
            (m_nState == CLEAN || m_nState == DIRTY))
         {
            InstanceList list = new InstanceArrayList(0);

            setValueDirect(nOrdinal, list);
            list.setAssociation(this, attribute, true);

            return list;
         }

         load(attribute.getSymbol());

         return getValueDirect(nOrdinal);
      }
      finally
      {
         --m_nLoadingDepth;
      }
   }

   /**
    * @see nexj.core.meta.Accessor#getValue(java.lang.String)
    */
   public Object getValue(String sName)
   {
      load();

      Attribute attribute = m_metaclass.findAttribute(sName);

      if (attribute == null)
      {
         Object value = findAnnotation(sName, Undefined.VALUE);

         if (value != Undefined.VALUE)
         {
            return value;
         }

         // This will throw an exception
         attribute = m_metaclass.getAttribute(sName);
      }

      if (attribute.isStatic())
      {
         return m_metaclass.getValue(attribute.getOrdinal());
      }

      return getValue(attribute.getOrdinal());
   }

   /**
    * @see nexj.core.meta.Accessor#getValue(java.lang.String, java.lang.String)
    */
   public Object getValue(String sName, String sFallbackName)
   {
      load();

      Attribute attribute = m_metaclass.findAttribute(sName);

      if (attribute == null)
      {
         if (sFallbackName == null)
         {
            return null;
         }

         attribute = m_metaclass.getAttribute(sFallbackName);
      }

      if (attribute.isStatic())
      {
         return m_metaclass.getValue(attribute.getOrdinal());
      }

      return getValue(attribute.getOrdinal());
   }

   /**
    * Converts a value to the type of a given attribute.
    * @param attribute The attribute.
    * @param value The value.
    * @return The converted value.
    */
   protected Object convert(Attribute attribute, Object value)
   {
      Type type = attribute.getType();

      if (attribute.isCollection())
      {
         if (value != null)
         {
            if (type.isPrimitive())
            {
               List list = (List)value;
               List convertedList = null;

               for (int i = 0, n = list.size(); i < n; ++i)
               {
                  Object original = list.get(i);

                  if (convertedList == null)
                  {
                     Primitive primitive = Primitive.primitiveOf(original);

                     if (type != primitive)
                     {
                        convertedList = new ArrayList(list.size());

                        for (int k = 0; k < i; ++k)
                        {
                           convertedList.add(list.get(k));
                        }

                        convertedList.add(((Primitive)type).getConverter(primitive).invoke(original));
                     }
                  }
                  else
                  {
                     convertedList.add(type.convert(original));
                  }
               }

               return (convertedList != null) ? convertedList : list;
            }

            InstanceList list;

            if (value instanceof InstanceList)
            {
               list = (InstanceList)value;
            }
            else if (value instanceof Collection)
            {
               list = new InstanceArrayList((Collection)value);
            }
            else if (value instanceof Pair)
            {
               Pair pair = (Pair)value;

               list = new InstanceArrayList(Pair.length(pair));

               do
               {
                  list.add(type.convert(pair.getHead()));
                  pair = pair.getNext();
               }
               while (pair != null);

               return list;
            }
            else if (value instanceof Object[])
            {
               list = new InstanceArrayList(Arrays.asList((Object[])value));
            }
            else
            {
               throw new TypeConversionException(type);
            }

            for (int i = 0, n = list.getCount(); i < n; ++i)
            {
               type.convert(list.get(i));
            }

            return list;
         }
      }

      return type.convert(value);
   }

   /**
    * Converts a calculated value and assigns reverse association attributes.
    * @param attribute The instance attribute.
    * @param value The value to set.
    * @return The converted value.
    */
   protected Object setCalcValue(Attribute attribute, Object value)
   {
      value = convert(attribute, value);

      if (m_context.getGeneration() == InvocationContext.GEN_NEW || m_nState >= NEW)
      {
         if (attribute.isCollection())
         {
            setValueDirect(attribute.getOrdinal(), value);

            if (!attribute.getType().isPrimitive())
            {
               if (value != null)
               {
                  InstanceList list = (InstanceList)value;

                  if (list.getContainer() == null)
                  {
                     list.setLazy(false);
                     list.setAssociation(this, attribute, true);
                  }

                  setAliases(list);
               }
            }
         }
         else
         {
            Attribute reverse = attribute.getReverse();

            if (reverse != null)
            {
               int nRevOrdinal = reverse.getOrdinal();

               Instance instance = (Instance)value;

               setValueDirect(attribute.getOrdinal(), instance);

               if (instance != null)
               {
                  instance.associate(nRevOrdinal, this, true);
               }
            }
            else
            {
               setValueDirect(attribute.getOrdinal(), value);
            }
         }
      }

      return value;
   }

   /**
    * @see nexj.core.meta.Accessor#setValueDirect(int, java.lang.Object)
    */
   public void setValueDirect(int nOrdinal, Object value)
   {
      assert !isLazy();

      switch (m_context.getGeneration())
      {
         case InvocationContext.GEN_NEW:
            m_valueArray[nOrdinal] = value;
            break;

         case InvocationContext.GEN_PRE:
            setPreValueDirect(nOrdinal, value);
            break;

         case InvocationContext.GEN_OLD:
            setOldValueDirect(nOrdinal, value);
            break;

         default:
            throw new IllegalStateException("Invalid generation");
      }
   }

   /**
    * @see nexj.core.meta.Accessor#getValueDirect(int)
    */
   public Object getValueDirect(int nOrdinal)
   {
      assert !isLazy();

      switch (m_context.getGeneration())
      {
         case InvocationContext.GEN_NEW:
            return m_valueArray[nOrdinal];

         case InvocationContext.GEN_PRE:
            return getPreValueDirect(nOrdinal);

         case InvocationContext.GEN_OLD:
            return getOldValueDirect(nOrdinal);

         default:
            throw new IllegalStateException("Invalid generation");
      }
   }

   /**
    * @see nexj.core.meta.Accessor#invalidate(int, Object)
    */
   public void invalidate(int nOrdinal, Object value)
   {
      load();

      if (m_nState == CLEAN)
      {
         Attribute attribute = m_metaclass.getInstanceAttribute(nOrdinal);

         if (!attribute.isCollection() && value != Undefined.VALUE)
         {
            if (attribute.isPersistent())
            {
               setDirty();
            }
            else
            {
               if (m_uow != null)
               {
                  m_uow.accumulateChange(this);
               }
               else if (m_context.getUnitOfWork() != null)
               {
                  m_context.getUnitOfWork().accumulateChange(this);
               }
               else
               {
                  m_context.accumulateChange(this, Instance.CLEAN);
               }
            }
         }
      }
      else if (isOverridden(nOrdinal))
      {
         return;
      }

      setValueDirect(nOrdinal, value);
   }

   /**
    * Gets an old (before update) value by ordinal number.
    * @param nOrdinal The attribute ordinal number.
    * @return The old value.
    */
   public Object getOldValue(int nOrdinal)
   {
      assert !isLazy();

      byte nGenerationSaved = m_context.getGeneration();

      try
      {
         m_context.setGeneration(InvocationContext.GEN_OLD);

         return getValue(nOrdinal);
      }
      finally
      {
         m_context.setGeneration(nGenerationSaved);
      }
   }

   /**
    * Gets an object attribute value by name.
    * @param sName The attribute name.
    * @return The attribute value.
    */
   public Object getOldValue(String sName)
   {
      load();

      Attribute attribute = m_metaclass.getAttribute(sName);

      if (attribute.isStatic())
      {
         return m_metaclass.getValue(attribute.getOrdinal());
      }

      return getOldValue(attribute.getOrdinal());
   }

   /**
    * Sets an old (before update) value by ordinal number, without side effects.
    * @param nOrdinal The attribute ordinal number.
    * @param value The value to set.
    */
   public void setOldValueDirect(int nOrdinal, Object value)
   {
      assert !isLazy();

      if (m_oldValueArray != null)
      {
         if (m_valueArray[nOrdinal] == Undefined.VALUE)
         {
            m_valueArray[nOrdinal] = value;
         }

         if (m_preValueArray[nOrdinal] == Undefined.VALUE)
         {
            m_preValueArray[nOrdinal] = value;
         }

         m_oldValueArray[nOrdinal] = value;
      }
      else
      {
         m_valueArray[nOrdinal] = value;
      }
   }

   /**
    * Gets an old (before update) value by ordinal number, without side effects.
    * @param nOrdinal The attribute ordinal number.
    * @return The old value.
    */
   public Object getOldValueDirect(int nOrdinal)
   {
      assert !isLazy();

      Object value = (m_oldValueArray != null) ? m_oldValueArray[nOrdinal] : m_valueArray[nOrdinal];

      if (value instanceof InstanceList)
      {
         InstanceList list = (InstanceList)value;

         if (list.isDirty())
         {
            value = m_context.getUnitOfWork().getOldCollection(list);

            if (m_oldValueArray != null)
            {
               m_oldValueArray[nOrdinal] = value;
            }
         }
      }

      return value;
   }

   /**
    * Sets a previous (since the last life cycle event) value by ordinal number.
    * @param nOrdinal The attribute ordinal number.
    * @return The previous value.
    */
   public Object getPreValue(int nOrdinal)
   {
      assert !isLazy();

      byte nGenerationSaved = m_context.getGeneration();

      try
      {
         m_context.setGeneration(InvocationContext.GEN_PRE);

         return getValue(nOrdinal);
      }
      finally
      {
         m_context.setGeneration(nGenerationSaved);
      }
   }

   /**
    * Sets a previous (since the last life cycle event) value by name.
    * @param sName The attribute name.
    * @return The previous value.
    */
   public Object getPreValue(String sName)
   {
      load();

      Attribute attribute = m_metaclass.getAttribute(sName);

      if (attribute.isStatic())
      {
         return m_metaclass.getValue(attribute.getOrdinal());
      }

      return getPreValue(attribute.getOrdinal());
   }

   /**
    * Sets a previous (since the last life cycle event) value by ordinal number,
    * without side effects.
    * @param nOrdinal The attribute ordinal number.
    * @param value The value to set.
    */
   public void setPreValueDirect(int nOrdinal, Object value)
   {
      assert !isLazy();

      if (m_preValueArray != null)
      {
         m_preValueArray[nOrdinal] = value;
      }
      else
      {
         m_valueArray[nOrdinal] = value;
      }
   }

   /**
    * Gets a previous (since the last life cycle event) value by ordinal number,
    * without side effects.
    * @param nOrdinal The attribute ordinal number.
    * @return The previous value.
    */
   public Object getPreValueDirect(int nOrdinal)
   {
      assert !isLazy();

      Object value = (m_preValueArray != null) ? m_preValueArray[nOrdinal] : m_valueArray[nOrdinal];

      if (value instanceof InstanceList)
      {
         InstanceList list = (InstanceList)value;

         if (list.isDirty())
         {
            value = m_context.getUnitOfWork().getPreCollection(list);

            if (m_preValueArray != null)
            {
               m_preValueArray[nOrdinal] = value;
            }
         }
      }

      return value;
   }

   /**
    * Sets a new (after update) value by ordinal number, without side effects.
    * @param nOrdinal The attribute ordinal number.
    * @param value The value to set.
    */
   public void setNewValueDirect(int nOrdinal, Object value)
   {
      assert !isLazy();

      m_valueArray[nOrdinal] = value;
   }

   /**
    * Gets a new (after update) value by ordinal number, without side effects.
    * @param nOrdinal The attribute ordinal number.
    * @return The new value.
    */
   public Object getNewValueDirect(int nOrdinal)
   {
      assert !isLazy();

      return m_valueArray[nOrdinal];
   }

   /**
    * Initializes the annotation map optimized for a given number annotations.
    * @param nCount The estimated annotation count.
    */
   public void initAnnotations(int nCount)
   {
      if (m_annotationMap == null)
      {
         m_annotationMap = new PropertyHashTab(nCount);
      }
   }

   /**
    * Sets an annotation.
    * @param sName The annotation name.
    * @param value The annotation value.
    */
   public void setAnnotation(String sName, Object value)
   {
      if (m_annotationMap == null)
      {
         m_annotationMap = new PropertyHashTab();
      }

      m_annotationMap.put(sName, value);
   }

   /**
    * Finds an annotation by name.
    * @param sName The annotation name.
    * @return The annotation, or null if not found.
    */
   public Object findAnnotation(String sName)
   {
      return findAnnotation(sName, null);
   }

   /**
    * Finds an annotation by name.
    * @param sName The annotation name.
    * @return The annotation, or null if not found.
    */
   public Object findAnnotation(String sName, Object defaultValue)
   {
      if (m_annotationMap == null)
      {
         return defaultValue;
      }

      return m_annotationMap.findValue(sName, defaultValue);
   }

   /**
    * @return The annotation iterator.
    */
   public PropertyIterator getAnnotationIterator()
   {
      if (m_annotationMap == null)
      {
         return PropertyHashTab.EMPTY_ITERATOR;
      }

      return m_annotationMap.getIterator();
   }

   /**
    * @return The annotation count.
    */
   public int getAnnotationCount()
   {
      if (m_annotationMap == null)
      {
         return 0;
      }

      return m_annotationMap.size();
   }

   /**
    * @see nexj.core.meta.Accessor#invoke(java.lang.String, java.lang.Object[])
    */
   public Object invoke(String sName, Object[] args)
   {
      load();

      Event event = (Event)m_metaclass.getSelector(sName).getMember((args != null) ? args.length : 0);

      return event.invoke((event.isStatic()) ? (Accessor)m_metaclass : this, args, m_context.getMachine());
   }

   /**
    * @see nexj.core.meta.Accessor#invoke(java.lang.String, nexj.core.scripting.Pair)
    */
   public Object invoke(String sName, Pair args)
   {
      load();

      Event event = (Event)m_metaclass.getSelector(sName).getMember(Pair.length(args));

      return event.invoke((event.isStatic()) ? (Accessor)m_metaclass : this, args, m_context.getMachine());
   }

   /**
    * @see nexj.core.meta.Accessor#invoke(java.lang.String)
    */
   public Object invoke(String sName)
   {
      return invoke(sName, (Object[])null);
   }

   /**
    * @see nexj.core.util.PropertyMap#findValue(java.lang.String)
    */
   public Object findValue(String sName)
   {
      return findValue(sName, null);
   }

   /**
    * @see nexj.core.util.PropertyMap#findValue(java.lang.String, java.lang.Object)
    */
   public Object findValue(String sName, Object defaultValue)
   {
      if (isLazy())
      {
         return findAnnotation(sName, defaultValue);
      }

      Attribute attribute = m_metaclass.findAttribute(sName);

      if (attribute == null)
      {
         return findAnnotation(sName, defaultValue);
      }

      Object value;

      if (attribute.isStatic())
      {
         value = m_metaclass.getValueDirect(attribute.getOrdinal());
      }
      else
      {
         value = getValueDirect(attribute.getOrdinal());
      }

      if (value instanceof Undefined)
      {
         value = defaultValue;
      }

      return value;
   }

   /**
    * @see nexj.core.util.PropertyMap#hasValue(java.lang.String)
    */
   public boolean hasValue(String sName)
   {
      if (isLazy())
      {
         return false;
      }

      Attribute attribute = m_metaclass.findAttribute(sName);

      if (attribute == null)
      {
         return false;
      }

      if (attribute.isStatic())
      {
         return !(m_metaclass.getValueDirect(attribute.getOrdinal()) instanceof Undefined);
      }

      return !(getValueDirect(attribute.getOrdinal()) instanceof Undefined);
   }

   /**
    * @see nexj.core.util.PropertyMap#getValueCount()
    */
   public int getValueCount()
   {
      if (isLazy())
      {
         return 0;
      }

      int nCount = 0;

      for (int i = 0, n = m_valueArray.length; i < n; ++i)
      {
         if (!(getValueDirect(i) instanceof Undefined))
         {
            ++nCount;
         }
      }

      return nCount;
   }

   /**
    * @see nexj.core.util.PropertyMap#getIterator()
    */
   public PropertyIterator getIterator()
   {
      return new PropertyIterator()
      {
         private int m_nNext = -1;
         private int m_nCur;

         {
            incr();
         }

         private void incr()
         {
            m_nCur = m_nNext++;

            if (m_valueArray != null)
            {
               while (m_nNext < m_valueArray.length)
               {
                  if (!(getValueDirect(m_nNext) instanceof Undefined))
                  {
                     break;
                  }

                  ++m_nNext;
               }
            }
         }

         public boolean hasNext()
         {
            return m_valueArray != null && m_nNext < m_valueArray.length;
         }

         public Object next()
         {
            if (m_valueArray != null && m_nNext < m_valueArray.length)
            {
               incr();

               return m_metaclass.getInstanceAttribute(m_nCur).getName();
            }

            throw new java.util.NoSuchElementException();
         }

         public void remove()
         {
            Instance.this.setValueDirect(m_nCur, Undefined.VALUE);
         }

         public String getName()
         {
            return m_metaclass.getInstanceAttribute(m_nCur).getName();
         }

         public Object getValue()
         {
            return Instance.this.getValue(m_nCur);
         }

         public void setValue(Object value)
         {
            Instance.this.setValue(m_nCur, value);
         }
      };
   }

   /**
    * @see nexj.core.util.PropertyMap#getClassName()
    */
   public String getClassName()
   {
      load();

      return m_metaclass.getName();
   }

   /**
    * @see nexj.core.scripting.Function#invoke(int, nexj.core.scripting.Machine)
    */
   public boolean invoke(int nArgCount, Machine machine)
   {
      if (nArgCount != 0)
      {
         Object sym = machine.getArg(0, nArgCount);

         if (sym instanceof Symbol)
         {
            if (isLazy() && sym != Symbol._OID)
            {
               load();
            }

            Selector sel = m_metaclass.findSelector(sym.toString());
            Member member = (sel == null) ? null : sel.findMember(nArgCount - 1);

            if (member == null)
            {
               Object value = Undefined.VALUE;

               if (nArgCount == 1)
               {
                  value = findAnnotation(sym.toString(), Undefined.VALUE);
               }

               if (value == Undefined.VALUE)
               {
                  // This should throw an exception
                  m_metaclass.getSelector(sym.toString()).getMember(nArgCount - 1);
               }

               machine.returnValue(value, nArgCount);

               return false;
            }

            if (member.isAttribute())
            {
               if (nArgCount == 1)
               {
                  machine.returnValue(
                     (member.isStatic()) ?
                        m_metaclass.getValue(((Attribute)member).getOrdinal()) :
                        getValue(((Attribute)member).getOrdinal()),
                     nArgCount);
               }
               else if (nArgCount == 2)
               {
                  Object value = machine.getArg(1, nArgCount);

                  if (member.isStatic())
                  {
                     m_metaclass.setValue(((Attribute)member).getOrdinal(), value);
                  }
                  else
                  {
                     setValue(((Attribute)member).getOrdinal(), value);
                  }

                  machine.returnValue(value, nArgCount);
               }
               else
               {
                  throw new ScriptingException("err.scripting.maxArgCount",
                     new Object[]{toString(),
                        Primitive.ONE_INTEGER,
                        Primitive.createInteger(nArgCount - 1)});
               }

               return false;
            }
            else
            {
               if (member.isStatic())
               {
                  machine.setArg(0, nArgCount, m_metaclass);
               }
               else
               {
                  machine.setArg(0, nArgCount, this);
               }

               if (m_metaclass.getLogger().isDebugEnabled())
               {
                  ((Event)member).dump(nArgCount, machine);
               }

               return ((Event)member).getFunction().invoke(nArgCount, machine);
            }
         }
      }
      else
      {
         throw new ScriptingException("err.scripting.minArgCount",
            new Object[]{m_metaclass.getName(),
               Primitive.ONE_INTEGER,
               Primitive.createInteger(nArgCount)});
      }

      throw new ScriptingException("err.scripting.funCall");
   }

   /**
    * Marks a calculated value as explicitly overridden.
    * @param nOrdinal The attribute ordinal number.
    */
   private void setOverridden(int nOrdinal)
   {
      if (m_overrideArray == null)
      {
         m_overrideArray = new byte[(m_metaclass.getInstanceAttributeCount() + 7) >> 3];
      }

      m_overrideArray[nOrdinal >> 3] |= 1 << (nOrdinal & 7);
   }

   /**
    * Checks whether a calculated value has been explicitly overridden.
    * @param nOrdinal The attribute ordinal number.
    */
   public boolean isOverridden(int nOrdinal)
   {
      return m_overrideArray != null && (m_overrideArray[nOrdinal >> 3] & 1 << (nOrdinal & 7)) != 0;
   }

   /**
    * Checks whether a value has been updated but not yet committed.
    * @param nOrdinal The attribute ordinal number.
    * @return True if the value has been updated.
    */
   public boolean isDirty(int nOrdinal)
   {
      assert !isLazy();

      if (m_nState == DELETED)
      {
         return false;
      }

      if (m_oldValueArray == null)
      {
         return m_nState == NEW;
      }

      Object value = getValueDirect(nOrdinal);

      if (value instanceof InstanceList &&
         ((InstanceList)value).isDirty())
      {
         return true;
      }

      if (m_metaclass.getInstanceAttribute(nOrdinal).isCalculated() &&
         !isOverridden(nOrdinal))
      {
         return false;
      }

      return !ObjUtil.equal(value, m_oldValueArray[nOrdinal]);
   }

   /**
    * Checks whether a value has been updated but not yet committed.
    * @param sName The attribute name.
    * @return True if the value has been updated.
    */
   public boolean isDirty(String sName)
   {
      if (isLazy())
      {
         return false;
      }

      Attribute attribute = m_metaclass.getAttribute(sName);

      if (attribute.isStatic())
      {
         return false;
      }

      return isDirty(attribute.getOrdinal());
   }

   /**
    * Checks whether a value has been updated since last life cycle event invocation.
    * @param nOrdinal The attribute ordinal number.
    * @return True if the value has been updated.
    */
   public boolean isUpdated(int nOrdinal)
   {
      assert !isLazy();

      Object value = getValueDirect(nOrdinal);

      if (value instanceof InstanceList &&
         ((InstanceList)value).isUpdated())
      {
         return true;
      }

      if (m_preValueArray == null)
      {
         return false;
      }

      if (m_metaclass.getInstanceAttribute(nOrdinal).isCalculated() &&
         !isOverridden(nOrdinal))
      {
         return false;
      }

      return !ObjUtil.equal(value, m_preValueArray[nOrdinal]);
   }

   /**
    * Checks whether a value has been updated since last life cycle event invocation.
    * @param sName The attribute name.
    * @return True if the value has been updated.
    */
   public boolean isUpdated(String sName)
   {
      if (isLazy())
      {
         return false;
      }

      Attribute attribute = m_metaclass.getAttribute(sName);

      if (attribute.isStatic())
      {
         return false;
      }

      return isUpdated(attribute.getOrdinal());
   }

   /**
    * Verifies that the instance contains valid data.
    * @param bFull True to perform full validation, false to perform only static validation.
    * @throws ValidationException if inconsistencies are found.
    */
   public void validate(boolean bFull) throws ValidationException
   {
      assert !isLazy();

      if (m_nState == NEW || m_nState == DIRTY)
      {
         PersistenceMapping persistenceMapping = getPersistenceMapping();
         PersistenceAdapter adapter = (persistenceMapping == null) ? null :
            (PersistenceAdapter)persistenceMapping.getDataSource()
               .getComponent().getInstance(m_context);
         ValidationException e = null;

         for (int i = 0, nCount = m_metaclass.getInstanceAttributeCount(); i != nCount; ++i)
         {
            Attribute attribute = m_metaclass.getInstanceAttribute(i);
            Object value = getValueDirect(i);

            if (value instanceof Undefined)
            {
               if ((m_nState == NEW || value == Invalid.VALUE) &&
                  (attribute.isPersistent() ||
                     (attribute.isRequired() ||
                        attribute.getValidationFunction() != null ||
                        m_nState == NEW && attribute.getInitializer() != Undefined.VALUE ||
                        attribute.isConstrained() && attribute.getEnumeration() != attribute.getType()) && bFull))
               {
                  value = getValue(i);
               }
               else
               {
                  continue;
               }
            }

            if (m_nState == DIRTY && !isDirty(i))
            {
               continue;
            }

            Type type = attribute.getType();

            if (attribute.isRequired())
            {
               boolean bMissing = (value == null);

               if (!bMissing)
               {
                  if (type.isPrimitive())
                  {
                     bMissing = (type == Primitive.STRING && value.equals(""));
                  }
                  else
                  {
                     bMissing = (attribute.isCollection() && ((InstanceList)value).isEmpty());
                  }
               }

               if (bMissing)
               {
                  e = createValidationException(e);

                  ValidationException x = new ValidationException("err.validation.requiredAttribute",
                     new Object[]{new StringId(attribute.getCaption()), new StringId(m_metaclass.getCaption())});

                  x.setClassName(m_metaclass.getName());
                  x.setOIDHolder(this);
                  e.addException(attribute.getName(), x);
               }
            }

            if (attribute.getMaxLength() > 0)
            {
               int nLength = 0;

               if (value instanceof String)
               {
                  nLength = ((String)value).length();
               }
               else if (value instanceof Binary)
               {
                  nLength = ((Binary)value).getData().length;
               }

               if (nLength > attribute.getMaxLength())
               {
                  e = createValidationException(e);

                  ValidationException x = new ValidationException("err.validation.maxDataLength",
                     new Object[]{Primitive.createInteger(attribute.getMaxLength()),
                        new StringId(attribute.getCaption()), new StringId(m_metaclass.getCaption())});

                  x.setClassName(m_metaclass.getName());
                  x.setOIDHolder(this);
                  e.addException(attribute.getName(), x);
               }
            }

            if (bFull)
            {
               if (attribute.isConstrained() &&
                  attribute.getEnumeration() != type &&
                  value != null && e == null)
               {
                  if (((InstanceList)attribute.getEnumeration().invoke("read",
                     new Object[]{null, (type.isPrimitive()) ?
                        Pair.attribute(Metaclass.ENUMERATION_VALUE).eq(value) :
                        new Pair(Symbol.AT).eq(value), null, Primitive.createInteger(-1),
                        Primitive.ZERO_INTEGER, Boolean.FALSE})).isEmpty())
                  {
                     e = createValidationException(e);

                     ValidationException x = new ValidationException("err.validation.enumerationValue",
                        new Object[]{new StringId(attribute.getEnumeration().getCaption()),
                           new StringId(attribute.getCaption()), new StringId(m_metaclass.getCaption())});

                     x.setClassName(m_metaclass.getName());
                     x.setOIDHolder(this);
                     e.addException(attribute.getName(), x);
                  }
               }

               if (attribute.getValidationFunction() != null && e == null)
               {
                  Object result = m_context.getMachine().invoke(attribute.getValidationFunction(), this, value, null);

                  if (result != null)
                  {
                     ValidationException x = null;

                     if (result instanceof Boolean)
                     {
                        if (!((Boolean)result).booleanValue())
                        {
                           x = new ValidationException("err.validation.valueRange",
                              new Object[]{new StringId(attribute.getCaption()), new StringId(m_metaclass.getCaption())});
                        }
                     }
                     else if (result instanceof String)
                     {
                        x = new ValidationException((String)result);
                     }
                     else if (result instanceof Pair)
                     {
                        x = new ValidationException((String)((Pair)result).getHead(),
                              Pair.toArray(((Pair)result).getNext()));
                     }

                     if (x != null)
                     {
                        x.setClassName(m_metaclass.getName());
                        x.setOIDHolder(this);
                        e = createValidationException(e);
                        e.addException(attribute.getName(), x);
                     }
                  }
               }
            }

            if (type.isPrimitive() && attribute.isPersistent() && adapter != null)
            {
               try
               {
                  adapter.validate(persistenceMapping.getAttributeMapping(attribute), value);
               }
               catch (ValidationException x)
               {
                  x.setClassName(m_metaclass.getName());
                  x.setOIDHolder(this);
                  e = createValidationException(e);
                  e.addException(attribute.getName(), x);
               }
            }
         }

         if (bFull && m_metaclass.getValidationFunction() != null && e == null)
         {
            Object result = m_context.getMachine().invoke(m_metaclass.getValidationFunction(), new Pair(this));

            if (result != null)
            {
               if (result instanceof Boolean)
               {
                  if (!((Boolean)result).booleanValue())
                  {
                     e = createValidationException(null);
                  }
               }
               else if (result instanceof String)
               {
                  e = new ValidationException((String)result);
               }
               else if (result instanceof Pair)
               {
                  e = new ValidationException((String)((Pair)result).getHead(),
                        Pair.toArray(((Pair)result).getNext()));
               }

               if (e != null)
               {
                  e.setClassName(m_metaclass.getName());
                  e.setOIDHolder(this);
               }
            }
         }

         if (e != null)
         {
            throw e;
         }
      }
   }

   /**
    * Helper method to create an invalid instance validation exception.
    * @param e The validation exception, can be null to create one.
    * @return The validation exception, or e if it was not null.
    */
   private ValidationException createValidationException(ValidationException e)
   {
      if (e == null)
      {
         e = new ValidationException("err.validation.invalidInstance",
            new Object[]{new StringId(m_metaclass.getCaption())});
         e.setClassName(m_metaclass.getName());
         e.setOIDHolder(this);
      }

      return e;
   }

   /**
    * Lazy-loads the initial instance state.
    */
   public void load()
   {
      assert m_oid == null ||
         m_nState != CLEAN && m_nState != DIRTY ||
         getPersistenceMapping() == null ||
         m_context.findInstance(m_metaclass, m_oid) == this :
         "Obsolete " + this;

      if (isLazy())
      {
         load((Pair)null, false);
      }
   }

   /**
    * Loads a lazy instance without throwing an optimistic locking
    * exception if it does not exist in the persistent store
    * (it can still throw the exception otherwise).
    * @return True if the instance has been loaded.
    * @see #load()
    */
   public boolean tryLoad()
   {
      assert m_oid == null ||
         m_nState != CLEAN && m_nState != DIRTY ||
         getPersistenceMapping() == null ||
         m_context.findInstance(m_metaclass, m_oid) == this :
         "Obsolete " + this;

      if (isLazy())
      {
         load((Pair)null, true);

         return !isLazy();
      }

      return true;
   }

   /**
    * Determines if an attribute value is loaded.
    * @param value The attribute value.
    * @param attribute The attribute object.
    * @param attributes The attributes to test, in read association format.
    * @return True if the attribute value has been loaded.
    */
   private boolean isLoaded(Object value, Attribute attribute, Pair attributes)
   {
      if (value instanceof Instance)
      {
         return ((Instance)value).isLoaded(attributes);
      }

      if (value instanceof InstanceList)
      {
         InstanceList list = (InstanceList)value;

         if (list.isLazy() || list.isWeak())
         {
            return false;
         }

         for (int i = 0, n = list.getCount(); i < n; ++i)
         {
            Instance instance = list.getInstance(i);

            if (instance == null || !instance.isLoaded(attributes))
            {
               return false;
            }
         }
      }

      return true;
   }

   /**
    * Determines if the persistent attributes on an instance are loaded.
    * @param attributes The attributes to test, in read association format.
    * @return True if all the attributes are loaded.
    */
   private boolean isLoaded(Pair attributes)
   {
      if (isLazy())
      {
         return (attributes == null);
      }

      for (Pair pair = attributes; pair != null; pair = pair.getNext())
      {
         Object head = pair.getHead();
         Pair assoc;
         Attribute attribute;

         if (head instanceof Pair)
         {
            assoc = (Pair)head;

            Symbol sym = (Symbol)assoc.getHead();

            assoc = assoc.getNext();

            if (sym == Symbol.ATAT)
            {
               if (m_metaclass.getMetadata().getMetaclass(((Symbol)assoc.getHead()).getName())
                     .isUpcast(m_metaclass) && !isLoaded(assoc.getNext()))
               {
                  return false;
               }

               continue;
            }

            attribute = m_metaclass.getAttribute(sym);
         }
         else
         {
            if (head == null)
            {
               continue;
            }

            assoc = null;
            attribute = m_metaclass.getAttribute((Symbol)head);
         }

         if (!attribute.isStatic())
         {
            Object value = getValueDirect(attribute.getOrdinal());

            if (!(value instanceof Undefined))
            {
               if (!isLoaded(value, attribute, assoc))
               {
                  return false;
               }
            }
            else
            {
               if (attribute.isPersistent() && value == Undefined.VALUE)
               {
                  return false;
               }
            }
         }
      }

      return true;
   }

   /**
    * Preprocesses and eliminates the load attributes and determines if a read operation is needed (m_bRead flag).
    * @param attributes The attributes to load, in read association format. This list is modified by the method.
    * @param tail The pair to which to append the attributes.
    * @param bPersistent True to compute the persistent read flag (m_bRead).
    * @return The first pair of the preprocessed attributes, or the new tail if tail is not null.
    */
   private Pair preload(Pair attributes, Pair tail, boolean bPersistent)
   {
      for (Pair pair = attributes, prev = null; pair != null; pair = pair.getNext())
      {
         Object head = pair.getHead();
         Pair assoc;
         Attribute attribute;

         if (head instanceof Pair)
         {
            assoc = (Pair)head;

            Symbol sym = (Symbol)assoc.getHead();

            assoc = assoc.getNext();

            if (sym == Symbol.ATAT)
            {
               if (m_metaclass.getMetadata().getMetaclass(((Symbol)assoc.getHead()).getName()).isUpcast(m_metaclass))
               {
                  if (tail == null)
                  {
                     pair.setHead(null);
                     pair = preload(assoc.getNext(), pair, bPersistent);

                     if (pair.getHead() == null && tail == null)
                     {
                        if (prev == null)
                        {
                           attributes = pair.getNext();
                        }
                        else
                        {
                           prev.setTail(pair.getTail());
                        }
                     }
                  }
                  else
                  {
                     tail = preload(assoc.getNext(), tail, bPersistent);
                  }
               }
               else
               {
                  if (tail == null)
                  {
                     if (prev == null)
                     {
                        attributes = pair.getNext();
                     }
                     else
                     {
                        prev.setTail(pair.getTail());
                     }
                  }
               }

               continue;
            }

            if (!bPersistent)
            {
               continue;
            }

            attribute = m_metaclass.getAttribute(sym);
         }
         else
         {
            if (head == null)
            {
               if (tail == null)
               {
                  if (prev == null)
                  {
                     attributes = pair.getNext();
                  }
                  else
                  {
                     prev.setTail(pair.getTail());
                  }
               }

               continue;
            }

            if (!bPersistent)
            {
               continue;
            }

            assoc = null;
            attribute = m_metaclass.getAttribute((Symbol)head);
         }

         if (attribute.isStatic())
         {
            if (tail == null)
            {
               if (prev == null)
               {
                  attributes = pair.getNext();
               }
               else
               {
                  prev.setTail(pair.getTail());
               }
            }
         }
         else
         {
            Object value = getValueDirect(attribute.getOrdinal());

            if (!(value instanceof Undefined))
            {
               if (isLoaded(value, attribute, assoc))
               {
                  if (tail == null)
                  {
                     if (prev == null)
                     {
                        attributes = pair.getNext();
                     }
                     else
                     {
                        prev.setTail(pair.getTail());
                     }
                  }
               }
               else
               {
                  if (tail == null)
                  {
                     prev = pair;
                  }
                  else if (tail.getHead() == null)
                  {
                     tail.setHead(pair.getHead());
                  }
                  else
                  {
                     Pair copy = new Pair(pair.getHead(), tail.getTail());

                     tail.setTail(copy);
                     tail = copy;
                  }

                  if (attribute.isPersistent())
                  {
                     m_nFlags |= LOAD_READ;
                  }
               }
            }
            else
            {
               if (tail == null)
               {
                  prev = pair;
               }
               else if (tail.getHead() == null)
               {
                  tail.setHead(pair.getHead());
               }
               else
               {
                  Pair copy = new Pair(pair.getHead(), tail.getTail());

                  tail.setTail(copy);
                  tail = copy;
               }

               if ((m_nFlags & LOAD_READ) == 0 && attribute.isPersistent() && value == Undefined.VALUE)
               {
                  m_nFlags |= LOAD_READ;
               }
            }
         }
      }

      if (tail != null)
      {
         return tail;
      }

      return attributes;
   }

   /**
    * Loads the specified attributes from a persistent store.
    * NOTE: This is for internal use only. Use invoke("load", ...) for general purpose coding.
    * @param attributes The attributes to load, in read association format. This list is *modified* by the method.
    * @param bSafe True to suppress exceptions is the instance does not exist.
    * @return The modified attribute list.
    */
   public Pair load(Pair attributes)
   {
      return load(attributes, false);
   }

   /**
    * Loads the specified attributes from a persistent store.
    * NOTE: This is for internal use only. Use invoke("load", ...) for general purpose coding.
    * @param attributes The attributes to load, in read association format. This list is *modified* by the method.
    * @param bSafe True to suppress exceptions if the instance does not exist.
    * @return The modified attribute list.
    */
   public Pair load(Pair attributes, boolean bSafe)
   {
      boolean bRead = isLazy();
      PersistenceMapping mapping = getPersistenceMapping();

      // Optimization: Avoid load when invoking event on lazy instance whose metaclass is not polymorphic
      if (bRead && mapping != null && !mapping.isTypeCodeDispatched() && mapping.getLockingAttribute() == null)
      {
         setMetaclass(m_metaclass);
         bRead = false;
      }

      if (m_nState != NEW && m_nState != DELETED && mapping != null)
      {
         boolean bLoad = bRead;

         if (!bRead)
         {
            m_nFlags &= ~LOAD_READ;
            attributes = preload(attributes, null, true);
            bRead = (m_nFlags & LOAD_READ) != 0;
         }

         if (bRead)
         {
            Query query = Query.createRead(m_metaclass, attributes,
               new Pair(Symbol.AT).eq(m_oid), null, -1, 0, false,
               Query.SEC_NONE, m_context);

            for (int i = 0, nCount = m_metaclass.getInstanceAttributeCount(); i < nCount; ++i)
            {
               Attribute attribute = m_metaclass.getInstanceAttribute(i);

               if ((isLazy() || getValueDirect(attribute.getOrdinal()) == Undefined.VALUE) &&
                  !attribute.isLazy())
               {
                  query.addAttribute(Query.ASSOC_QUERY, attribute, null, false, Query.OUTPUT_EAGER);
               }
            }

            if (query.getFirstOutputField() != null &&
               (query.getFirstOutputField().getNext() != null ||
                  query.getFirstOutputField().getAttribute() !=
                  mapping.getLockingAttribute()) ||
               query.getAssocCount(Query.ASSOC_QUERY) > 0)
            {
               if (s_logger.isDebugEnabled())
               {
                  String sMsg = "Loading attributes " + attributes + " of " + this;

                  if (s_logger.isDumpEnabled())
                  {
                     StackTrace t = new StackTrace();

                     m_context.getMachine().updateStackTrace(t);
                     s_logger.dump(sMsg, t);
                  }
                  else
                  {
                     s_logger.debug(sMsg);
                  }
               }

               if (query.read().isEmpty())
               {
                  if (bSafe)
                  {
                     return attributes;
                  }

                  // The instance could not be found
                  if (isLazy())
                  {
                     // Just the OID was available - check the referrer
                     for (Iterator itr = m_context.getInstanceRefIterator(); itr.hasNext();)
                     {
                        InstanceRef ref = (InstanceRef)itr.next();

                        if (ref != null)
                        {
                           Instance instance = ref.getInstance();

                           if (instance != null && !instance.isLazy() &&
                              instance.getState() != NEW && instance.getOID() != null)
                           {
                              Metaclass metaclass = instance.getMetaclass();

                              if (metaclass.getPersistenceMapping() != null)
                              {
                                 for (int i = 0, n = metaclass.getInstanceAttributeCount(); i < n; ++i)
                                 {
                                    Attribute attribute = metaclass.getInstanceAttribute(i);

                                    if (!attribute.isCollection() && attribute.isPersistent() &&
                                       instance.getOldValueDirect(i) == this)
                                    {
                                       if (!Query.createRead(metaclass, new Pair(attribute.getSymbol()),
                                          new Pair(Symbol.AT).eq(instance), null, -1, 0, false, Query.SEC_NONE,
                                          m_context).read().isEmpty() &&
                                          instance.getOldValueDirect(i) == this)
                                       {
                                          throw new AssociationIntegrityException(instance, attribute.getName());
                                       }

                                       throw new OptimisticLockException(this);
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
                  else if (!Query.createRead(getLazyMetaclass(), null, new Pair(Symbol.AT).eq(m_oid),
                     null, -1, 0, false, Query.SEC_NONE, m_context).read().isEmpty())
                  {
                     throw new AssociationIntegrityException(this);
                  }

                  throw new OptimisticLockException(this);
               }
            }

            if (bLoad)
            {
               attributes = preload(attributes, null, false);
            }
         }
         else
         {
            bRead = true;
         }
      }
      else
      {
         attributes = preload(attributes, null, false);
      }

      for (; attributes != null; attributes = attributes.getNext())
      {
         Object head = attributes.getHead();
         Pair assoc = (head instanceof Pair) ? (Pair)head : null;
         Attribute attribute = m_metaclass.getAttribute(
            (Symbol)((assoc != null) ? assoc.getHead() : head));

         if (!attribute.isStatic())
         {
            Object value = getValueDirect(attribute.getOrdinal());

            if (value instanceof Undefined)
            {
               switch (m_nState)
               {
               case INIT:
               case NEW:
                  if (attribute.getInitializerFunction() != null)
                  {
                     setCalcValue(attribute,
                        m_context.getMachine().invoke(attribute.getInitializerFunction(), this, (Object[])null));

                     continue;
                  }

                  break;

               case CLEAN:
               case DIRTY:
                  if (attribute.getValueFunction() != null)
                  {
                     if (attribute.isCached())
                     {
                        setCalcValue(attribute,
                           m_context.getMachine().invoke(attribute.getValueFunction(), this, (Object[])null));
                     }

                     continue;
                  }

                  break;
               }

               if (attribute.isCollection())
               {
                  InstanceList list = new InstanceArrayList(0);

                  setValueDirect(attribute.getOrdinal(), list);
                  list.setAssociation(this, attribute, true);
                  list.setLazy(false);
               }
               else
               {
                  setValueDirect(attribute.getOrdinal(), null);
               }

               if (s_logger.isDebugEnabled())
               {
                  s_logger.debug("Uninitialized " + attribute + ", set to " + getValueDirect(attribute.getOrdinal()));
               }
            }
            else if (value != null && !bRead)
            {
               if (assoc != null && assoc.getTail() != null)
               {
                  if (value instanceof InstanceList)
                  {
                     InstanceList list = (InstanceList)value;

                     for (int i = 0, n = list.getCount(); i < n; ++i)
                     {
                        Instance instance = list.getInstance(i);

                        if (instance != null && !instance.isLoading())
                        {
                           instance.invoke("load", assoc.getNext());
                        }
                     }
                  }
                  else if (value instanceof Instance)
                  {
                     Instance instance = (Instance)value;

                     if (!instance.isLoading())
                     {
                        instance.invoke("load", assoc.getNext());
                     }
                  }
               }
            }
         }
      }

      return attributes;
   }

   /**
    * Locks the instance.
    */
   public void lock()
   {
      if (m_oid != null)
      {
         load();

         switch (m_nState)
         {
            case CLEAN:
            case DIRTY:
            case DELETED:
               Pair attributes = null;

               if (m_nState == CLEAN)
               {
                  for (int i = m_metaclass.getInstanceAttributeCount() - 1; i >= 0; --i)
                  {
                     Attribute attribute = m_metaclass.getInstanceAttribute(i);

                     if (!(getValueDirect(i) instanceof Undefined) && !attribute.isCollection())
                     {
                        attributes = new Pair(attribute.getSymbol(), attributes);
                     }
                  }
               }

               Query.createRead(m_metaclass, attributes, new Pair(Symbol.AT).eq(m_oid),
                  null, -1, 0, true, Query.SEC_NONE, m_context).read();

               break;
         }
      }
   }

   /**
    * Deletes the instance.
    */
   public void delete()
   {
      if (m_uow != null && m_uow != m_context.getUnitOfWork() && m_context.isUnitOfWorkGlobal())
      {
         throw new TransactionException("err.runtime.wrongTransaction",
            new Object[]{getLazyClassName()});
      }

      deleteRelated();

      if (m_nState != DELETED)
      {
         setDeleted();
      }

      uncache();
      setEventInvoked(true);
   }

   /**
    * Invokes cascade delete logic on related objects.
    */
   public void deleteRelated()
   {
      load();

      if (m_nState != DELETED)
      {
         byte nFlagsSaved = m_nFlags;

         m_nFlags |= UPDATEABLE;

         try
         {
            // check cancellation conditions first
            for (int i = m_metaclass.getInstanceAttributeCount() - 1; i >= 0; --i)
            {
               Attribute attribute = m_metaclass.getInstanceAttribute(i);

               if (attribute.getCascadeMode() == Attribute.CASCADE_CANCEL)
               {
                  boolean bCancel;

                  if (attribute.isCollection())
                  {
                     InstanceList list = (InstanceList)getValue(attribute.getOrdinal());

                     bCancel = (list != null && !list.isEmpty());
                  }
                  else
                  {
                     Instance instance = (Instance)getValue(attribute.getOrdinal());

                     bCancel = (instance != null && instance.tryLoad());
                  }

                  if (bCancel)
                  {
                     ConstraintViolationException e = new ConstraintViolationException(
                        "err.persistence.cascadeCancel",
                        new Object[]{new StringId(m_metaclass.getCaption()),
                           getName(), new StringId(attribute.getCaption())});

                     e.setClassName(getClassName());
                     e.setOIDHolder(this);

                     throw e;
                  }
               }
            }

            // then do the rest of the cascade operations
            for (int i = m_metaclass.getInstanceAttributeCount() - 1; i >= 0; --i)
            {
               Attribute attribute = m_metaclass.getInstanceAttribute(i);

               switch (attribute.getCascadeMode())
               {
                  case Attribute.CASCADE_NONE:
                     if (attribute.isSymmetric())
                     {
                        Object value = getValueDirect(attribute.getOrdinal());

                        if (value != null)
                        {
                           if (value == Undefined.VALUE &&
                              attribute.isReverseInverseDependencyPersistent())
                           {
                              value = getValue(attribute.getOrdinal());
                           }

                           if (!(value instanceof Undefined))
                           {
                              if (attribute.isCollection())
                              {
                                 if (value != null)
                                 {
                                    InstanceList list = (InstanceList)value;

                                    // Full load of the list is triggered by size()
                                    if (list.size() == 0)
                                    {
                                       continue;
                                    }

                                    do
                                    {
                                       int nOrdinal = list.size() - 1;
                                       Instance instance = list.getInstance(nOrdinal);
                                       byte nInstanceFlagsSaved = instance.m_nFlags;

                                       instance.m_nFlags |= UPDATEABLE;

                                       try
                                       {
                                          list.remove(nOrdinal);
                                       }
                                       finally
                                       {
                                          instance.m_nFlags &= ~UPDATEABLE;
                                          instance.m_nFlags |= nInstanceFlagsSaved & UPDATEABLE;
                                       }
                                    }
                                    while (!list.isEmpty());
                                 }
                              }
                              else
                              {
                                 if (value != null)
                                 {
                                    Instance instance = (Instance)value;

                                    if (instance.tryLoad())
                                    {
                                       byte nInstanceFlagsSaved = instance.m_nFlags;

                                       instance.m_nFlags |= UPDATEABLE;

                                       try
                                       {
                                          setValue(attribute.getOrdinal(), null);
                                       }
                                       finally
                                       {
                                          instance.m_nFlags &= ~UPDATEABLE;
                                          instance.m_nFlags |= nInstanceFlagsSaved & UPDATEABLE;
                                       }
                                    }
                                    else
                                    {
                                       setValueDirect(attribute.getOrdinal(), null);
                                    }
                                 }
                              }
                           }
                        }
                     }

                     continue;

                  case Attribute.CASCADE_DELETE:
                  case Attribute.CASCADE_CLEAR:
                     break;

                  default:
                     continue;
               }

               if (attribute.isCollection())
               {
                  InstanceList list = (InstanceList)getValue(attribute.getOrdinal());

                  if (list == null)
                  {
                     continue;
                  }

                  switch (attribute.getCascadeMode())
                  {
                     case Attribute.CASCADE_DELETE:
                        // Full load of the list is triggered by size()
                        if (list.size() == 0)
                        {
                           continue;
                        }

                        do
                        {
                           int nOrdinal = list.size() - 1;
                           Instance instance = list.getInstance(nOrdinal);
                           byte nInstanceFlagsSaved = instance.m_nFlags;

                           // Remove the association first to avoid circular deletions in business logic
                           instance.m_nFlags |= UPDATEABLE;

                           try
                           {
                              list.remove(nOrdinal);
                           }
                           finally
                           {
                              instance.m_nFlags &= ~UPDATEABLE;
                              instance.m_nFlags |= nInstanceFlagsSaved & UPDATEABLE;
                           }

                           instance.invoke("delete", (Object[])null);
                        }
                        while (!list.isEmpty());

                        break;

                     case Attribute.CASCADE_CLEAR:
                        // Full load of the list is triggered by size()
                        if (list.size() == 0)
                        {
                           continue;
                        }

                        list.clear();
                        break;
                  }
               }
               else
               {
                  Instance instance = (Instance)getValue(attribute.getOrdinal());

                  if (instance == null)
                  {
                     continue;
                  }

                  if (instance.tryLoad())
                  {
                     switch (attribute.getCascadeMode())
                     {
                        case Attribute.CASCADE_DELETE:
                           // Remove the association first to avoid circular deletions in business logic
                           byte nInstanceFlagsSaved = instance.m_nFlags;

                           instance.m_nFlags |= UPDATEABLE;

                           try
                           {
                              setValue(attribute.getOrdinal(), null);
                           }
                           finally
                           {
                              instance.m_nFlags &= ~UPDATEABLE;
                              instance.m_nFlags |= nInstanceFlagsSaved & UPDATEABLE;
                           }

                           instance.invoke("delete", (Object[])null);
                           break;

                        case Attribute.CASCADE_CLEAR:
                           setValue(attribute.getOrdinal(), null);
                           break;
                     }
                  }
                  else
                  {
                     setValueDirect(attribute.getOrdinal(), null);
                  }
               }
            }
         }
         finally
         {
            m_nFlags &= ~UPDATEABLE;
            m_nFlags |= nFlagsSaved & UPDATEABLE;
         }
      }
   }

   /**
    * Replicates the instance into the relevant fragments.
    */
   public void replicate()
   {
      PersistenceMapping mapping = getPersistenceMapping();

      if (mapping != null)
      {
         byte nReplication = mapping.getFragmentReplication();

         if (nReplication != PersistenceMapping.REPLICATION_NONE)
         {
            DataSource dataSource = mapping.getDataSource();

            if (dataSource.getFragmentCount() != 1)
            {
               UnitOfWork uow = m_uow;

               if (uow == null)
               {
                  uow = m_context.getUnitOfWork();
               }

               if (nReplication == PersistenceMapping.REPLICATION_UNICAST)
               {
                  if (getFragmentName() == null)
                  {
                     Attribute attribute = mapping.getFragmentAttribute();

                     if (attribute != null)
                     {
                        uow.addReplicationFragment(this, (String)((attribute.isStatic()) ?
                           m_metaclass : (Accessor)this).getValue(attribute.getOrdinal()));
                     }
                  }
                  else
                  {
                     uow.addReplicationFragment(this, null);
                  }
               }
               else if (nReplication == PersistenceMapping.REPLICATION_BROADCAST)
               {
                  if (getFragmentName() == null)
                  {
                     for (Iterator itr = dataSource.getFragmentIterator(); itr.hasNext();)
                     {
                        uow.addReplicationFragment(this, ((DataSourceFragment)itr.next()).getName());
                     }
                  }
                  else
                  {
                     throw new PersistenceException("err.persistence.broadcastFragment",
                        new Object[]{m_metaclass.getName(), m_context.getFragmentName()});
                  }
               }
            }
         }
      }
   }

   /**
    * Flushes the cache for this instance.
    */
   public void uncache()
   {
      if (m_uow != null && (m_oid != null || m_nState == NEW))
      {
         load();
         uncache(m_metaclass, m_oid, m_uow);

         Metaclass root = m_metaclass.getPersistenceRoot();

         for (int i = 0, n = root.getPersistenceAliasCount(); i < n; ++i)
         {
            Metaclass alias = root.getPersistenceAlias(i);
            PersistenceMapping mapping = alias.getPersistenceMapping();
            Attribute typeCodeAttr = mapping.getTypeCodeAttribute();

            if (typeCodeAttr != null)
            {
               Metaclass metaclass = mapping.findMetaclassByTypeCode(getValue(typeCodeAttr.getOrdinal()));

               if (metaclass != null)
               {
                  alias = metaclass;
               }
            }

            uncache(alias, m_oid, m_uow);
         }
      }
   }

   /**
    * Flushes the instance cache.
    * @param metaclass The class object.
    * @param oid The OID. Can be null.
    * @param uow The unit of work.
    */
   protected static void uncache(Metaclass metaclass, OID oid, UnitOfWork uow)
   {
      PersistenceMapping mapping = metaclass.getPersistenceMapping();

      if (mapping != null)
      {
         if (mapping.getCaching() != PersistenceMapping.CACHING_NONE)
         {
            if (oid != null)
            {
               uow.uncache(getInstanceKey(metaclass, oid), UnitOfWork.CACHE_UNPARTITIONED);
            }
         }

         Metaclass root = metaclass.getPersistenceRoot();

         do
         {
            if (mapping.getCaching() == PersistenceMapping.CACHING_CLASS)
            {
               uow.uncache(getClassKey(metaclass, null), UnitOfWork.CACHE_UNPARTITIONED);
            }

            metaclass = metaclass.getBase();

            if (metaclass == null)
            {
               break;
            }

            mapping = metaclass.getPersistenceMapping();

            if (metaclass.getPersistenceRoot() != root)
            {
               break;
            }
         }
         while (mapping != null);
      }
   }

   /**
    * Creates a class cache key.
    * @param metaclass The class object.
    * @param sKey Additional key part. Null for the main class cache.
    * @return The class cache key.
    */
   public static Object getClassKey(Metaclass metaclass, String sKey)
   {
      return new Pair(metaclass.getSymbol(), sKey);
   }

   /**
    * Creates an instance cache key.
    * @param metaclass The class object.
    * @param oid The instance OID.
    * @return The instance cache key.
    */
   public static Object getInstanceKey(Metaclass metaclass, OID oid)
   {
      return new Pair(metaclass.getSymbol(), oid);
   }

   /**
    * @return The instance name, which is the value
    * of the name attribute, if any is defined.
    */
   public String getName()
   {
      if (isLazy())
      {
         try
         {
            load();
         }
         catch (PersistenceException e)
         {
         }
      }

      Attribute attribute = m_metaclass.getNameAttribute();

      if (attribute != null)
      {
         try
         {
            return (String)getValue(attribute.getOrdinal());
         }
         catch (PersistenceException e)
         {
         }
      }

      if (m_oid != null)
      {
         return m_oid.toString();
      }

      return "";
   }

   /**
    * @see nexj.core.util.Printable#printOn(nexj.core.util.PrintWriter)
    */
   public void printOn(PrintWriter writer) throws IOException
   {
      writer.write("Instance<");
      writer.write((m_metaclass != null) ? m_metaclass.getName() : null);
      writer.write(", ");
      writer.write(String.valueOf(m_oid));
      writer.write(", ");
      writer.write(STATE_NAME_ARRAY[m_nState]);
      writer.write(">(");

      if (m_metaclass != null && !isLazy() && writer.addObject(this))
      {
         boolean bDirty = false;

         for (int i = 0; i < m_metaclass.getInstanceAttributeCount(); ++i)
         {
            Object value = getValueDirect(i);

            if (value instanceof Undefined)
            {
               continue;
            }

            if (bDirty)
            {
               writer.write(", ");
            }
            else
            {
               bDirty = true;
            }

            printOn(writer, m_metaclass.getInstanceAttribute(i).getName(), value);
         }

         if (m_annotationMap != null)
         {
            for (Lookup.Iterator itr = m_annotationMap.iterator(); itr.hasNext();)
            {
               if (bDirty)
               {
                  writer.write(", ");
               }
               else
               {
                  bDirty = true;
               }

               itr.next();
               printOn(writer, (String)itr.getKey(), itr.getValue());
            }
         }

         writer.removeObject(this);
      }

      writer.write(')');
   }

   /**
    * Prints an attribute value on an writer.
    * @param writer The writer.
    * @param sName The attribute name.
    * @param value The attribute value.
    */
   protected static void printOn(PrintWriter writer, String sName, Object value) throws IOException
   {
      writer.write(sName);
      writer.write('=');

      if (value instanceof Instance)
      {
         Instance instance = (Instance)value;

         writer.write("Instance<");
         writer.write((instance.m_metaclass != null) ? instance.m_metaclass.getName() : null);
         writer.write(", ");
         writer.write(String.valueOf(instance.m_oid));
         writer.write(", ");
         writer.write(STATE_NAME_ARRAY[instance.m_nState]);
         writer.write('>');
      }
      else if (value instanceof Collection)
      {
         writer.write("Collection(");
         writer.print((value instanceof InstanceList) ? ((InstanceList)value).getCount() : ((Collection)value).size());
         writer.write(')');
      }
      else
      {
         writer.print(value);
      }
   }

   /**
    * @see java.lang.Object#toString()
    */
   public String toString()
   {
      return PrintWriter.toString(this);
   }
}
TOP

Related Classes of nexj.core.runtime.Instance

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.