Package nexj.core.meta

Source Code of nexj.core.meta.Attribute$DirectInverseDependency

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

import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import nexj.core.meta.integration.EndpointPart;
import nexj.core.meta.persistence.AttributeMapping;
import nexj.core.meta.persistence.ClassMapping;
import nexj.core.meta.persistence.PersistenceMapping;
import nexj.core.meta.ui.AttributeMeta;
import nexj.core.meta.ui.ClassMeta;
import nexj.core.rpc.TransferObject;
import nexj.core.runtime.InstanceList;
import nexj.core.runtime.SecurityViolationException;
import nexj.core.scripting.Compiler;
import nexj.core.scripting.ConstPair;
import nexj.core.scripting.Function;
import nexj.core.scripting.Intrinsic;
import nexj.core.scripting.Machine;
import nexj.core.scripting.PCodeFunction;
import nexj.core.scripting.Pair;
import nexj.core.scripting.Symbol;
import nexj.core.util.Captioned;
import nexj.core.util.EmptyIterator;
import nexj.core.util.ExceptionHolder;
import nexj.core.util.HashHolder;
import nexj.core.util.Holder;
import nexj.core.util.IdentityHashHolder;
import nexj.core.util.Invalid;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.ObjUtil;
import nexj.core.util.PropertyMap;
import nexj.core.util.UncheckedException;
import nexj.core.util.Undefined;

/**
* The attribute metadata.
*/
public final class Attribute extends Member implements AttributeMeta, Captioned, Cloneable, Serializable, EndpointPart
{
   // constants
  
   /**
    * Serial Id.
    */
   private static final long serialVersionUID = 916910521644664010L;

   /**
    * Inherit the cascade property, or CASCADE_NONE without inheritance.
    */
   public final static byte CASCADE_DEFAULT = 0;
  
   /**
    * No cascading is performed.
    */
   public final static byte CASCADE_NONE = 1;
  
   /**
    * Cascading deletes are performed.
    */
   public final static byte CASCADE_DELETE = 2;
  
   /**
    * Cascading clearing is performed.
    */
   public final static byte CASCADE_CLEAR = 3;
  
   /**
    * The delete is cancelled if the association is available.
    */
   public final static byte CASCADE_CANCEL = 4;

   /**
    * The arguments for the value expression.
    */
   private final static Pair VALUE_ARGUMENTS = new ConstPair(Symbol.THIS);

   /**
    * The arguments for the validation expression.
    */
   private final static Pair VALIDATION_ARGUMENTS = ConstPair.list(Symbol.THIS, Symbol.VALUE);

   /**
    * The current attribute global variable used by macros.
    */
   private final static Symbol SYS_CURRENT_ATTRIBUTE = Symbol.define("sys:current-attribute");

   /**
    * S-expression designating this object, suitable for the object query.
    */
   private final static Pair THIS = new ConstPair(Symbol.AT);

   // attributes

   /**
    * The attribute ordinal number in the class (0..n).
    * Class and instance attributes have independent numbering.
    */
   private int m_nOrdinal = -1;

   /**
    * The maximum attribute value length.
    */
   private int m_nMaxLength;

   /**
    * True if the attribute cannot be null.
    */
   private boolean m_bRequired;

   /**
    * True if the attribute holds a collection of values.
    */
   private boolean m_bCollection;
  
   /**
    * The constrained enumeration flag.
    */
   private boolean m_bConstrained;

   /**
    * The read-only value flag.
    */
   private boolean m_bReadOnly;

   /**
    * Whether dependency information is known exactly.
    */
   private boolean m_bFullDependency;

   /**
    * The caching flag.
    */
   private byte m_nCached = -1;

   /**
    * The cascade mode - one of the CASCADE_* constants.
    */
   private byte m_nCascadeMode;

   /**
    * The derived association where expression. Can be null.
    */
   private Object m_where;

   /**
    * The attribute caption string id.
    */
   private String m_sCaption;

   // associations

   /**
    * The attribute type.
    */
   private Type m_type;

   /**
    * The enumeration class.
    */
   private Metaclass m_enumeration;

   /**
    * The attribute corresponding to the reverse association.
    */
   private Attribute m_reverse;

   /**
    * The inherited attribute with a compatible mapping from the tompost class.
    */
   private Attribute m_persistenceRoot = this;

   /**
    * The next attribute with the same persistence mapping in a circular list.
    */
   private Attribute m_alias;

   /**
    * The read privilege.
    */
   private PrimitivePrivilege m_readPrivilege;

   /**
    * The update privilege.
    */
   private PrimitivePrivilege m_updatePrivilege;

   /**
    * The expression used to calculate the attribute value.
    * Can be Undefined.VALUE.
    */
   private Object m_value = Undefined.VALUE;

   /**
    * The value function.
    */
   private Function m_valueFunction;

   /**
    * The expression used to calculate the initial attribute value.
    * Can be Undefined.VALUE.
    */
   private Object m_initializer = Undefined.VALUE;

   /**
    * The initializer function.
    */
   private Function m_initializerFunction;

   /**
    * Validation expression taking this and value arguments and returning #f,
    * a string id or a list of format arguments if the attribute is invalid.
    * Can be Undefined.VALUE.
    */
   private Object m_validation = Undefined.VALUE;

   /**
    * The validation function.
    */
   private Function m_validationFunction;

   /**
    * The attribute dependency list: (attr1 attr2 (assoc1 attr1_1 ... attr1_N) ... attrN).
    */
   private Pair m_dependency;

   /**
    * The cumulative dependency (incl. subclasses): (attr1 attr2 (assoc1 attr1_1 ...) (class2 attr2_1 ...) ...).
    */
   private Pair m_cumulativeDependency;

   /**
    * The attribute inverse dependency list.
    */
   private InverseDependencyList m_inverseDependency;

   /**
    * The attribute alternative order by list: ((attr1 . bAscFlag1) ... (attrN . bAscFlagN)).
    */
   private Pair m_orderBy;

   /**
    * The overriding value declarator collection.
    * These are subclasses with different value expressions for the same attribute.
    */
   private List m_valueDeclaratorList; // of type Metaclass

   /**
    * The text position map for attribute's value, initializer, and validation
    */
   private Lookup m_textPosMap;

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

   /**
    * Constructs an attribute with a given name.
    * @param sName The attribute name.
    */
   public Attribute(String sName)
   {
      super(sName);
   }

   // operations

   /**
    * @return true
    * @see nexj.core.meta.Member#isAttribute()
    */
   public boolean isAttribute()
   {
     return true;
   }

   /**
    * Sets the attribute ordinal number in the class.
    * @param nOrdinal The attribute ordinal number in the class to set.
    */
   public void setOrdinal(int nOrdinal)
   {
      verifyNotReadOnly();
      m_nOrdinal = nOrdinal;
   }

   /**
    * @return The attribute ordinal number in the class.
    */
   public int getOrdinal()
   {
      return m_nOrdinal;
   }

   /**
    * Sets the maximum attribute value length.
    * @param nMaxLength The maximum attribute value length to set.
    */
   public void setMaxLength(int nMaxLength)
   {
      verifyNotReadOnly();
      m_nMaxLength = nMaxLength;
   }

   /**
    * @return The maximum attribute value length.
    */
   public int getMaxLength()
   {
      return m_nMaxLength;
   }
  
   /**
    * Sets the requiredness flag.
    * @param bRequired The requiredness flag to set.
    */
   public void setRequired(boolean bRequired)
   {
      verifyNotReadOnly();
      m_bRequired = bRequired;
   }

   /**
    * @return The requiredness flag.
    */
   public boolean isRequired()
   {
      return m_bRequired;
   }
  
   /**
    * Sets the collection flag.
    * @param bCollection The collection flag to set.
    */
   public void setCollection(boolean bCollection)
   {
      verifyNotReadOnly();
      m_bCollection = bCollection;
   }

   /**
    * @return The collection flag.
    */
   public boolean isCollection()
   {
      return m_bCollection;
   }
  
   /**
    * Sets the read-only value flag.
    * @param bReadOnly The read-only value flag to set.
    */
   public void setReadOnly(boolean bReadOnly)
   {
      verifyNotReadOnly();
      m_bReadOnly = bReadOnly;
   }

   /**
    * @return The read-only value flag.
    */
   public boolean isReadOnly()
   {
      return m_bReadOnly;
   }

   /**
    * Sets the caching flag.
    * @param cached The caching flag to set.
    */
   public void setCached(Boolean cached)
   {
      verifyNotReadOnly();
     
      if (cached == null)
      {
         m_nCached = -1;
      }
      else
      {
         setCached(cached.booleanValue());
      }
   }

   /**
    * Sets the caching flag.
    * @param bCached The caching flag to set.
    */
   public void setCached(boolean bCached)
   {
      verifyNotReadOnly();
      m_nCached = (bCached) ? (byte)1 : (byte)0;

      if (!bCached)
      {
         m_bReadOnly = true;
      }
   }

   /**
    * @return The caching flag.
    */
   public boolean isCached()
   {
      return m_nCached != 0;
   }

   /**
    * @return True if the attribute should be omitted from lazy loads and caching. 
    */
   public boolean isLazy()
   {
      return m_bStatic || m_bCollection || !isPersistent() ||
         !m_type.isPrimitive() && !((ClassMapping)getPersistenceMapping()).isInner();
   }

   /**
    * Sets the cascade mode - one of the CASCADE_* constants.
    * @param nCascadeMode The cascade mode to set.
    */
   public void setCascadeMode(byte nCascadeMode)
   {
      verifyNotReadOnly();
     
      if (nCascadeMode != CASCADE_DEFAULT && nCascadeMode != CASCADE_NONE)
      {
         if (m_type != null && m_type.isPrimitive())
         {
            throw new MetadataException("err.meta.primitiveCascadeMode", new Object[]{getName(), m_metaclass.getName()});
         }

         if (m_bStatic)
         {
            throw new MetadataException("err.meta.staticCascadeMode", new Object[]{getName(), m_metaclass.getName()});
         }
      }
     
      m_nCascadeMode = nCascadeMode;
   }

   /**
    * @return The cascade mode - one of the CASCADE_* constants.
    */
   public byte getCascadeMode()
   {
      return m_nCascadeMode;
   }
  
   /**
    * Sets the read privilege.
    * @param readPrivilege The read privilege to set.
    */
   public void setReadPrivilege(PrimitivePrivilege readPrivilege)
   {
      verifyNotReadOnly();
      m_readPrivilege = readPrivilege;
   }

   /**
    * @return The read privilege.
    */
   public PrimitivePrivilege getReadPrivilege()
   {
      return m_readPrivilege;
   }

   /**
    * Sets the update privilege.
    * @param updatePrivilege The update privilege to set.
    */
   public void setUpdatePrivilege(PrimitivePrivilege updatePrivilege)
   {
      verifyNotReadOnly();
      m_updatePrivilege = updatePrivilege;
   }

   /**
    * @return The update privilege.
    */
   public PrimitivePrivilege getUpdatePrivilege()
   {
      return m_updatePrivilege;
   }

   /**
    * @see nexj.core.meta.Member#setAccessAttribute(nexj.core.meta.Attribute)
    */
   public void setAccessAttribute(Attribute accessAttribute)
   {
      if (accessAttribute != null && m_bStatic && !accessAttribute.isStatic())
      {
         MetadataValidationException e = new MetadataValidationException(
            "err.meta.accessAttributeAllocation",
            new Object[]{accessAttribute.getName(), getName(), m_metaclass.getName()});

         setProperties(e);

         throw e;
      }

      super.setAccessAttribute(accessAttribute);
   }

   /**
    * Sets the attribute value expression.
    * @param value The attribute value expression to set. Can be Undefined.VALUE.
    */
   public void setValue(Object value)
   {
      verifyNotReadOnly();
      m_value = value;

      if (value == Undefined.VALUE)
      {
         m_nCached = (byte)1;
      }
   }

   /**
    * @return The attribute value expression.
    */
   public Object getValue()
   {
      return m_value;
   }

   /**
    * @return The value function.
    */
   public Function getValueFunction()
   {
      return m_valueFunction;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getValueDependencyAssociations()
    */
   public Pair getValueDependencyAssociations()
   {
      return getValueDependencyAssociations(getDependency());
   }

   /**
    * Change a dependency list from being "attribute list" form to a list of
    * association paths. Also AttributeMetas are replaced with their names.
    *
    * @param dep Dependencies
    * @return List of association paths
    */
   private Pair getValueDependencyAssociations(Pair dep)
   {
      Pair result = null;

      while (dep != null)
      {
         Object head = dep.getHead();

         if (head instanceof AttributeMeta)
         {
            result = new Pair(new Pair(((AttributeMeta)head).getName()), result);
         }
         else if (head instanceof Pair && ((Pair)head).getHead() instanceof AttributeMeta)
         {
            Pair next = (Pair)head;
            final String sAttr = ((AttributeMeta)next.getHead()).getName();

            next = getValueDependencyAssociations(next.getNext());

            while (next != null)
            {
               result = new Pair(new Pair(sAttr, next.getHead()), result);
               next = next.getNext();
            }
         }
         else
         {
            assert false;
         }

         dep = dep.getNext();
      }

      return result;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#isClientCalculable()
    */
   public boolean isClientCalculable()
   {
      return isFullDependency() && getValueFunction() != null && getDependency() != null;
   }

   /**
    * @return Whether the dependency information is known exactly - that it
    *         contains all run-time dependencies.
    */
   protected boolean isFullDependency()
   {
      return m_bFullDependency;
   }

   /**
    * Set the new value for whether dependency information is full.
    *
    * @param bFullDependency The new value for whether dependency is full
    * @see #isFullDependency()
    */
   protected void setFullDependency(boolean bFullDependency)
   {
      verifyNotReadOnly();
      m_bFullDependency = bFullDependency;
   }

   /**
    * Sets the initial attribute value expression.
    * @param initializer The initial attribute value expression to set. Can be Primitive.VALUE.
    */
   public void setInitializer(Object initializer)
   {
      verifyNotReadOnly();
      m_initializer = initializer;
   }

   /**
    * @return The initial attribute value expression.
    */
   public Object getInitializer()
   {
      return m_initializer;
   }
  
   /**
    * @return The initializer function.
    */
   public Function getInitializerFunction()
   {
      return m_initializerFunction;
   }
  
   /**
    * Sets the validation expression.
    * @param validation The validation expression to set.
    */
   public void setValidation(Object validation)
   {
      verifyNotReadOnly();
     
      if (validation != Undefined.VALUE && m_bStatic)
      {
         throw new MetadataException("err.meta.staticValidation",
            new Object[]{getName(), m_metaclass.getName()});
      }
     
      m_validation = validation;
   }

   /**
    * @return The validation expression.
    */
   public Object getValidation()
   {
      return m_validation;
   }

   /**
    * @return The validation function.
    */
   public Function getValidationFunction()
   {
      return m_validationFunction;
   }

   /**
    * Sets the text position map for the attribute's value, initializer, and validation.
    * @param textPosMap The attribute text position map to set.
    */
   public void setTextPositionMap(Lookup textPosMap)
   {
      verifyNotReadOnly();
      m_textPosMap = textPosMap;
   }

   /**
    * Copies the functions from a given attribute.
    * @param src The source attribute providing the functions.
    */
   public void setFunctions(Attribute src)
   {
      verifyNotReadOnly();
      m_valueFunction = src.m_valueFunction;
      m_initializerFunction = src.m_initializerFunction;
      m_validationFunction = src.m_validationFunction;
   }
  
   /**
    * Sets the attribute type.
    * @param type The attribute type to set.
    */
   public void setType(Type type)
   {
      verifyNotReadOnly();
      m_type = type;
   }

   /**
    * @return The attribute type.
    */
   public Type getType()
   {
      return m_type;
   }

   /**
    * Sets the enumeration class.
    * @param enumeration The enumeration class to set.
    */
   public void setEnumeration(Metaclass enumeration)
   {
      verifyNotReadOnly();
      m_enumeration = enumeration;
   }

   /**
    * @return The enumeration class.
    */
   public Metaclass getEnumeration()
   {
      return m_enumeration;
   }

   /**
    * Sets the constrained enumeration flag.
    * @param bConstrained The constrained enumeration flag to set.
    */
   public void setConstrained(boolean bConstrained)
   {
      verifyNotReadOnly();
      m_bConstrained = bConstrained;
   }

   /**
    * @return The constrained enumeration flag.
    */
   public boolean isConstrained()
   {
      return m_bConstrained;
   }
  
   /**
    * Sets the derived association where clause.
    * @param where The derived association where clause to set.
    */
   public void setWhere(Object where)
   {
      verifyNotReadOnly();
     
      if (where != null && m_bStatic)
      {
         throw new MetadataException("err.meta.staticDerivedAssoc",
            new Object[]{getName(), m_metaclass.getName()});
      }

      m_where = where;
   }

   /**
    * @return The derived association where clause.
    */
   public Object getWhere()
   {
      return m_where;
   }

   /**
    * Sets the reverse association attribute.
    * @param reverse The reverse association attribute to set. Can be null.
    */
   public void setReverse(Attribute reverse)
   {
      verifyNotReadOnly();

      if (reverse != null)
      {
         if (reverse.m_type.isPrimitive() || m_type.isPrimitive())
         {
            throw new MetadataException("err.meta.primitiveReverseAttrib",
               new Object[]{reverse.getName(), getName(), m_metaclass.getName()});
         }
        
         if (!((Metaclass)reverse.m_type).isUpcast(m_metaclass) && !(m_metaclass instanceof Aspect) ||
            !reverse.m_metaclass.isUpcast((Metaclass)m_type) ||
            m_reverse != null && reverse.m_nOrdinal != m_reverse.m_nOrdinal ||
            m_bStatic || reverse.isStatic())
         {
            throw new MetadataException("err.meta.reverseAttribMismatch",
               new Object[]{reverse.getName(), reverse.m_metaclass.getName(),
                  getName(), m_metaclass.getName()});
         }
      }

      m_reverse = reverse;

      for (int nDerived = 0, nDerivedCount = m_metaclass.getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         m_metaclass.getDerived(nDerived).getDerivedAttribute(this).setReverse(reverse);
      }
   }

   /**
    * @return The reverse association attribute.
    */
   public Attribute getReverse()
   {
      return m_reverse;
   }

   /**
    * Determines if this attribute is the reverse of a given attribute.
    * @param attribute The attribute relative to which to perform the test.
    * @return True if this attribute is the reverse of the supplied attribute.
    */
   public boolean isReverseOf(Attribute attribute)
   {
      return !m_bStatic && attribute.m_reverse != null &&
         attribute.m_reverse.m_nOrdinal == m_nOrdinal;
   }

   /**
    * @return True if the reverse of the reverse attribute is this attribute
    * or an attribute overridden by this attribute.
    */
   public boolean isSymmetric()
   {
      return m_reverse != null && isReverseOf(m_reverse);
   }

   /**
    * Sets the inherited attribute with a compatible mapping from the topmost class.
    * @param persistenceRoot The inherited attribute with a compatible mapping from the topmost class to set.
    */
   public void setPersistenceRoot(Attribute persistenceRoot)
   {
      verifyNotReadOnly();
      m_persistenceRoot = persistenceRoot;
   }

   /**
    * @return The inherited attribute with a compatible mapping from the topmost class.
    */
   public Attribute getPersistenceRoot()
   {
      return m_persistenceRoot;
   }
  
   /**
    * Sets the next attribute with the same persistence mapping in a circular list.
    * @param alias The next attribute with the same persistence mapping in a circular list to set.
    */
   public void setAlias(Attribute alias)
   {
      verifyNotReadOnly();
      m_alias = alias;
   }

   /**
    * @return The next attribute with the same persistence mapping in a circular list.
    */
   public Attribute getAlias()
   {
      return m_alias;
   }
  
   /**
    * Sets the attribute dependency list.
    * @param dependency The attribute dependency list to set.
    */
   public void setDependency(Pair dependency)
   {
      verifyNotReadOnly();
      m_dependency = dependency;
      m_cumulativeDependency = dependency;
   }

   /**
    * @return The attribute dependency list.
    */
   public Pair getDependency()
   {
      return m_dependency;
   }

   /**
    * @return The cumulative dependency (incl. subclasses).
    */
   public Pair getCumulativeDependency()
   {
      return m_cumulativeDependency;
   }
  
   /**
    * @return The attribute inverse dependency list.
    */
   public InverseDependencyList getInverseDependency()
   {
      return m_inverseDependency;
   }
  
   /**
    * Lazy-creates an inverse dependency list.
    * @return The attribute inverse dependency list.
    */
   protected InverseDependencyList createInverseDependency()
   {
      verifyNotReadOnly();
     
      if (m_inverseDependency == null)
      {
         m_inverseDependency = new InverseDependencyList();
      }
     
      return m_inverseDependency;
   }

   /**
    * @return True if the inverse dependency contains persistent attributes.
    */
   public boolean isInverseDependencyPersistent()
   {
      return m_inverseDependency != null && m_inverseDependency.isPersistent();
   }
  
   /**
    * @return True if the inverse dependency of the reverse attribute containt persistent attributes.
    */
   public boolean isReverseInverseDependencyPersistent()
   {
      return m_reverse != null && m_reverse.m_inverseDependency != null &&
         m_reverse.m_inverseDependency.isPersistent();
   }
  
   /**
    * Sets the attribute alternative order by list - ((attr1 . bAscFlag1) ... (attrN . bAscFlagN)).
    * @param orderBy The attribute alternative order by list to set.
    */
   public void setOrderBy(Pair orderBy)
   {
      verifyNotReadOnly();
      m_orderBy = orderBy;
   }

   /**
    * @return The attribute alternative order by list - ((attr1 . bAscFlag1) ... (attrN . bAscFlagN)).
    */
   public Pair getOrderBy()
   {
      return m_orderBy;
   }
  
   /**
    * Sets the attribute caption string id.
    * @param sCaption The attribute caption string id to set.
    */
   public void setCaption(String sCaption)
   {
      verifyNotReadOnly();
      m_sCaption = sCaption;
   }

   /**
    * @return The attribute caption string id.
    */
   public String getCaption()
   {
      return (m_sCaption == null) ? m_sName : m_sCaption;
   }

   /**
    * Derive member values from base attribute.
    * @param base The attribute to inherit from (null == no base).
    */
   public void deriveFrom(Attribute base)
   {
      verifyNotReadOnly();

      if (m_sCaption == null)
      {
         setCaption(base.m_sCaption); // inherit parent caption if own is unset
      }
   }

   /**
    * @return The persistence mapping of the attribute, or null if none.
    */
   protected AttributeMapping getPersistenceMapping()
   {
      return findPersistenceMapping(null, false);
   }

   /**
    * Finds an attribute mapping that is compatible with a given persistence mapping.
    * @param compatible The compatible mapping. Can be null to use the class mapping.
    * @param bInverse True to use the attribute type class.
    * @return The attribute mapping, or null if not found.
    */
   public AttributeMapping findPersistenceMapping(PersistenceMapping compatible, boolean bInverse)
   {
      assert !bInverse || !m_type.isPrimitive();

      PersistenceMapping mapping = ((bInverse) ? (Metaclass)m_type : m_metaclass).getPersistenceMapping();

      if (mapping != null)
      {
         if (compatible != null)
         {
            mapping = mapping.findMapping(compatible);
         }

         if (mapping != null)
         {
            if (bInverse)
            {
               PersistenceMapping persistenceMapping = m_metaclass.getPersistenceMapping();

               if (persistenceMapping != null)
               {
                  return persistenceMapping.findClassMapping(this, mapping);
               }
            }
            else
            {
               return mapping.getAttributeMapping(this);
            }
         }
      }

      return null;
   }

   /**
    * @return True if the attribute is persistent.
    */
   public boolean isPersistent()
   {
      return getPersistenceMapping() != null;
   }

   /**
    * @return True if the attribute is calculated.
    */
   public boolean isCalculated()
   {
      return m_value != Undefined.VALUE || m_dependency != null;
   }
  
   /**
    * Determines if this attribute is derivation compatible with a base attribute.
    * @param base The base attribute.
    * @return True if it is compatible.
    */
   public boolean isCompatibleWith(Attribute base)
   {
      return m_type.isPrimitive() == base.m_type.isPrimitive() &&
         ((m_type.isPrimitive()) ? m_type == base.m_type :
            ((Metaclass)base.m_type).isUpcast((Metaclass)m_type)) &&
         (m_bRequired == base.m_bRequired || !base.m_bRequired) &&
         m_reverse == base.m_reverse &&
         m_bStatic == base.m_bStatic &&
         m_bCollection == base.m_bCollection;
   }

   /**
    * Compiles the initializer and the value expressions to p-code.
    * @see nexj.core.meta.Member#compile(nexj.core.scripting.Machine)
    */
   public void compile(Machine machine)
   {
      verifyNotReadOnly();
      setCurrent();

      try
      {
         if (m_value != Undefined.VALUE)
         {
            m_valueFunction = compile(m_value, VALUE_ARGUMENTS, machine, "value");
         }

         if (m_initializer != Undefined.VALUE)
         {
            m_initializerFunction = compile(m_initializer, VALUE_ARGUMENTS, machine, "initializer");
         }
         else
         {
            m_initializerFunction = m_valueFunction;
         }
        
         if (m_validation != Undefined.VALUE)
         {
            m_validationFunction = compile(m_validation, VALIDATION_ARGUMENTS, machine, "validation");
         }
      }
      finally
      {
         setTextPositionMap(null);
         clearCurrent();
      }
   }

   /**
    * Compiles an expression to a p-code method.
    * @param expr The expression to compile.
    * @param args The expression formal argument list.
    * @param machine The VM to use for compilation.
    * @param sItem The item to use in an error message.
    */
   private PCodeFunction compile(Object expr, Pair args, Machine machine, String sItem)
   {
      try
      {
         return new Compiler().compile(
            new Pair(Symbol.LAMBDA, new Pair(args, new Pair(expr))),
            m_textPosMap, machine, false);
      }
      catch (Exception e)
      {
         MetadataValidationException x;

         if (e instanceof UncheckedException)
         {
            x = new MetadataValidationException((UncheckedException)e);
         }
         else
         {
            x = new MetadataValidationException("err.meta.attributeCompilation", e);
         }

         setProperties(x);
         x.setProperty("item", sItem);

         throw x;
      }
   }

   /**
    * Resolves the attribute dependency and order by references after it has been loaded.
    * @param machine The VM for macro expansion.
    */
   public void resolve(Machine machine)
   {
      verifyNotReadOnly();
      m_metaclass.setCurrent();
      setCurrent();

      try
      {
         Pair dep = new Pair(Boolean.TRUE);

         m_metaclass.dependency(m_value, m_bStatic, dep, null, machine);

         setDerivedDependency(
            Pair.nconc(dep.getNext(),
            resolveDependency(m_metaclass, m_dependency)), Intrinsic.isTrue(dep.getHead()));

         setDerivedOrderBy(resolveOrderBy(m_metaclass, m_orderBy));
      }
      finally
      {
         clearCurrent();
         m_metaclass.clearCurrent();
      }
   }

   /**
    * Sets the current attribute in the global environment.
    */
   protected void setCurrent()
   {
      m_metaclass.getMetadata().getGlobalEnvironment().defineVariable(SYS_CURRENT_ATTRIBUTE, this);
   }
  
   /**
    * Removes the current attribute from the global environment.
    */
   protected void clearCurrent()
   {
      m_metaclass.getMetadata().getGlobalEnvironment().removeVariable(SYS_CURRENT_ATTRIBUTE);
   }

   /**
    * Copies the dependency list and converts the symbols to attributes.
    * @param metaclass The metaclass where to look for attributes.
    * @param dep The dependency list to convert.
    * @return The converted dependency list.
    * @throws MetadataException if the dependency list is invalid.
    */
   private Pair resolveDependency(Metaclass metaclass, Pair dep) throws MetadataException
   {
      try
      {
         Pair first, last;

         for (first = last = null; dep != null; dep = dep.getNext())
         {
            Pair pair;

            if (dep.getHead() instanceof Pair)
            {
               Pair head = (Pair)dep.getHead();

               if (head == null || head.getHead() == null)
               {
                  throw new MetadataException("err.meta.attributeDep",
                     new Object[]{getName(), m_metaclass.getName()});
               }

               Attribute attribute = metaclass.getAttribute(((Symbol)head.getHead()).getName());
               Type type = attribute.getType();

               if (type.isPrimitive())
               {
                  throw new MetadataException("err.meta.attributeAssocDep",
                     new Object[]{attribute.getName(), attribute.m_metaclass.getName(),
                        getName(), m_metaclass.getName()});
               }

               pair = new Pair(new Pair(attribute, resolveDependency((Metaclass)type, head.getNext())));
            }
            else if (dep.getHead() instanceof Symbol)
            {
               pair = new Pair(metaclass.getAttribute(((Symbol)dep.getHead()).getName()));
            }
            else
            {
               throw new MetadataException("err.meta.attributeDep",
                  new Object[]{getName(), m_metaclass.getName()});
            }
           
            if (last == null)
            {
               first = last = pair;
            }
            else
            {
               last.setTail(pair);
               last = pair;
            }
         }

         return first;
      }
      catch (MetadataException e)
      {
         throw e;
      }
      catch (ClassCastException e)
      {
         throw new MetadataException("err.meta.attributeDep",
            new Object[]{getName(), getMetaclass().getName()});
      }
   }

   /**
    * Sets the dependency list in a derived attribute, if it is not overridden.
    * @param dep The dependency list to set.
    */
   private void setDerivedDependency(Pair dep, boolean bFullDependency)
   {
      setDependency(dep);
      setFullDependency(bFullDependency);

      if (s_logger.isDumpEnabled() && isClientCalculable())
      {
         s_logger.dump(m_metaclass.getName() + '.' + m_sName + " is eligible for client-side calculation. Dependencies: "
            + getValueDependencyAssociations());
      }

      for (int nDerived = 0, nDerivedCount = m_metaclass.getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         Attribute attribute = m_metaclass.getDerived(nDerived).getDerivedAttribute(this);

         if (attribute.m_declarator == m_declarator)
         {
            attribute.setDerivedDependency(dep, bFullDependency);
         }
      }
   }

   /**
    * Verifies the dependency list and computes the inverse dependency list.
    * @param depSet The set to which to add the attributes for the
    * inverse dependency calculation.
    */
   public void computeInverseDependency(Set depSet)
   {
      verifyNotReadOnly();

      if (m_dependency != null)
      {
         Holder set = new IdentityHashHolder();

         set.add(this);
         verifyDependency(m_metaclass, m_dependency, set);

         List stack = new ArrayList();

         stack.add(this);
         addInverseDependency(m_dependency, stack, depSet);
      }
   }

   /**
    * Adds an inverse dependency list to the attributes referenced in a dependency list.
    * @param dep The dependency list.
    * @param stack The stack containing the attributes in one branch of the dependency list.
    * @param depSet The set of the attributes, to which inverse dependencies have been added.
    */
   private static void addInverseDependency(Pair dep, List stack, Set depSet)
   {
      for (; dep != null; dep = dep.getNext())
      {
         Attribute attribute;

         if (dep.getHead() instanceof Pair)
         {
            Pair pair = (Pair)dep.getHead();
           
            attribute = (Attribute)pair.getHead();
           
            if (attribute.getReverse() != null)
            {
               stack.add(attribute);
               addInverseDependency(pair.getNext(), stack, depSet);
               stack.remove(stack.size() - 1);
            }
         }
         else
         {
            attribute = (Attribute)dep.getHead();
         }

         depSet.add(attribute);
         addInverseDependency(attribute.createInverseDependency(), stack, stack.size() - 1);

         for (int i = stack.size() - 2; i > 0; --i)
         {
            attribute = (Attribute)stack.get(i);
            addInverseDependency(attribute.createInverseDependency(), stack, i - 1);
         }
      }
   }

   /**
    * Augments an inverse dependency list.
    * @param depList The inverse dependency list to augment.
    * @param stack The stack with a single dependency list branch.
    * @param nLast The last reference index in the stack.
    * @return The augmented inverse dependency list.
    */
   private static void addInverseDependency(InverseDependencyList depList, List stack, int nLast)
   {
      Attribute reverse = (Attribute)stack.get(nLast);
     
      if (nLast > 0)
      {
         reverse = reverse.getReverse();
      }

      for (InverseDependency dep = depList.getDependency(); dep != null; dep = dep.getNext())
      {
         if (dep.getAttribute().getOrdinal() == reverse.getOrdinal())
         {
            if (dep.isIndirect())
            {
               if (nLast > 0)
               {
                  addInverseDependency((InverseDependencyList)dep, stack, nLast - 1);
               }

               if (dep.isPersistent())
               {
                  depList.setPersistent();
               }
            }

            return;
         }
      }

      if (nLast > 0)
      {
         IndirectInverseDependency dep = new IndirectInverseDependency(reverse);

         addInverseDependency(dep, stack, nLast - 1);
         depList.addDependency(dep);
      }
      else
      {
         depList.addDependency(new DirectInverseDependency(reverse));
      }
   }

   /**
    * Verifies that a dependency list does not contain bogus data.
    * @param metaclass The class relative to which the list is.
    * @param dep The dependency list.
    * @param identitySet The identity set of Attribute for detecting circular references.
    * @throws MetadataException if the verification fails.
    */
   private void verifyDependency(Metaclass metaclass, Pair dep, Holder identitySet) throws MetadataException
   {
      for (; dep != null; dep = dep.getNext())
      {
         Attribute attribute;

         if (dep.getHead() instanceof Pair)
         {
            Pair pair = (Pair)dep.getHead();

            attribute = (Attribute)pair.getHead();
            pair = pair.getNext();

            if (pair != null)
            {
               if (m_nCached < 0 && attribute.getReverse() == null)
               {
                  if (s_logger.isDebugEnabled())
                  {
                     s_logger.debug(m_metaclass.getName() + '.' + m_sName +
                        " uncached due to missing reverse of the dependency " +
                        attribute.getMetaclass().getName() + '.' + attribute.getName());
                  }

                  setCached(false);
               }

               verifyDependency((Metaclass)attribute.getType(), pair, identitySet);
            }
         }
         else
         {
            attribute = (Attribute)dep.getHead();
         }

         if (!identitySet.add(attribute))
         {
            throw new MetadataException("err.meta.attributeDepCycle",
               new Object[]{getName(), m_metaclass.getName()});
         }

         verifyDependency(metaclass, attribute.getDependency(), identitySet);
         identitySet.remove(attribute);
      }
   }

   /**
    * Adds the inverse dependency list to the derived attributes.
    * @param depList The inverse dependency list to add.
    */
   private void addDerivedInverseDependency(InverseDependencyList depList)
   {
      for (int nDerived = 0, nDerivedCount = m_metaclass.getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         Attribute attribute = m_metaclass.getDerived(nDerived).getDerivedAttribute(this);

         attribute.createInverseDependency().append(depList);
         attribute.addDerivedInverseDependency(depList);
      }
   }

   /**
    * Adds the inverse dependency list to the derived attributes for all the elements in the iterator.
    * @param itr The iterator containing the attributes to process.
    */
   public static void resolveInverseDependency(Iterator itr)
   {
      while (itr.hasNext())
      {
         Attribute attribute = (Attribute)itr.next();
        
         attribute.addDerivedInverseDependency(attribute.m_inverseDependency);
      }
   }

   /**
    * Invalidates all the attributes referenced in the inverse dependency list.
    * @param depList The inverse dependency list.
    * @param accessor The accessor to invalidate.
    * @param undef The new value to assign.
    */
   protected static void invalidateDependency(InverseDependencyList depList, Accessor accessor, Object undef)
   {
      if (accessor != null)
      {
         for (InverseDependency dep = depList.getDependency() ; dep != null; dep = dep.getNext())
         {
            Attribute attribute = dep.getAttribute();

            if (attribute.getMetaclass().isUpcast(accessor.getMetaclass()))
            {
               if (dep.isIndirect())
               {
                  Object value = accessor.getValueDirect(attribute.getOrdinal());

                  if (value == Undefined.VALUE && undef == Invalid.VALUE && dep.isPersistent())
                  {
                     value = accessor.getValue(attribute.getOrdinal());
                  }

                  if (!(value instanceof Undefined))
                  {
                     if (attribute.isCollection())
                     {
                        InstanceList list = (InstanceList)value;
                        int nCount = list.getCount();

                        for (int i = 0; i < nCount; ++i)
                        {
                           invalidateDependency((InverseDependencyList)dep, (Accessor)list.get(i), undef);
                        }
                     }
                     else
                     {
                        invalidateDependency((InverseDependencyList)dep, (Accessor)value, undef);
                     }
                  }
               }
               else
               {
                  attribute = accessor.getMetaclass().getDerivedAttribute(attribute);

                  if (undef != Undefined.VALUE || !attribute.isPersistent())
                  {
                     InverseDependencyList attrDepList = attribute.getInverseDependency();

                     boolean bDep = (attrDepList != null &&
                        (attrDepList.isPersistent() || accessor.getValueDirect(attribute.getOrdinal()) != undef));

                     accessor.invalidate(attribute.getOrdinal(), undef);

                     if (bDep)
                     {
                        invalidateDependency(attrDepList, accessor, undef);
                     }
                  }
               }
            }
         }
      }
   }

   /**
    * Invalidates all the attributes referenced in the inverse dependency list.
    * @param accessor The accessor to invalidate.
    * @param undef The new value to assign.
    */
   public void invalidateDependency(Accessor accessor, Object undef)
   {
      if (m_inverseDependency != null)
      {
         invalidateDependency(m_inverseDependency, accessor, undef);
      }
   }
  
   /**
    * Adds the specified dependency list to the cumulative dependency.
    * @param dep The dependency list to add.
    */
   public void addCumulativeDependency(Pair dep)
   {
      verifyNotReadOnly();
     
      Pair cumulativeDep = m_cumulativeDependency;

   loop:
      for (; dep != null; dep = dep.getNext())
      {
         Object value = dep.getHead();
         Attribute attr1 = (value instanceof Attribute) ? (Attribute)value : null;

         for (Pair pair = cumulativeDep; pair != null; pair = pair.getNext())
         {
            if (pair.getHead() == value)
            {
               continue loop;
            }

            if (attr1 != null && pair.getHead() instanceof Attribute)
            {
               Attribute attr2 = (Attribute)pair.getHead();

               if (attr1.getOrdinal() == attr2.getOrdinal() &&
                  attr1.isStatic() == attr2.isStatic() &&
                  attr1.getRootDeclarator() == attr2.getRootDeclarator())
               {
                  continue loop;
               }
            }
         }

         m_cumulativeDependency = new Pair(value, m_cumulativeDependency);
      }
   }

   /**
    * Adds a new overriding value declarator to the attribute.
    * @param valueDeclarator The value declarator to add.
    */
   protected void addValueDeclarator(Metaclass valueDeclarator)
   {
      verifyNotReadOnly();

      if (m_valueDeclaratorList == null)
      {
         m_valueDeclaratorList = new ArrayList(4);
      }

      m_valueDeclaratorList.add(valueDeclarator);
   }

   /**
    * Adds value declarators from a given attribute.
    * @param attribute The source attribute.
    */
   protected void addValueDeclarators(Attribute attribute)
   {
      verifyNotReadOnly();

      if (!ObjUtil.equal(m_value, attribute.getValue()))
      {
         addValueDeclarator(attribute.getMetaclass());
      }

      for (int i = 0, n = attribute.getValueDeclaratorCount(); i < n; ++i)
      {
         addValueDeclarator(attribute.getValueDeclarator(i));
      }
   }

   /**
    * Gets a value declarator by ordinal number.
    * @param nOrdinal The value declarator ordinal number (0-based).
    * @return The value declarator object.
    */
   public Metaclass getValueDeclarator(int nOrdinal)
   {
      return (Metaclass)m_valueDeclaratorList.get(nOrdinal);
   }

   /**
    * @return The value declarator count.
    */
   public int getValueDeclaratorCount()
   {
      if (m_valueDeclaratorList == null)
      {
         return 0;
      }

      return m_valueDeclaratorList.size();
   }

   /**
    * @return An iterator for the contained value declarator objects.
    */
   public Iterator getValueDeclaratorIterator()
   {
      if (m_valueDeclaratorList == null)
      {
         return EmptyIterator.getInstance();
      }

      return m_valueDeclaratorList.iterator();
   }

   /**
    * Determines if the value is overridden in a given set of classes.
    * @param metaclassSet The set to test, or null for all classes.
    * @return True if the value is overridden by a member of the set.
    */
   public boolean isValueOverridden(Set metaclassSet)
   {
      if (m_valueDeclaratorList != null)
      {
         for (int i = 0, n = m_valueDeclaratorList.size(); i < n; ++i)
         {
            if (metaclassSet == null || metaclassSet.contains(m_valueDeclaratorList.get(i)))
            {
               return true;
            }
         }
      }

      return isValueOverridden(m_dependency, metaclassSet);
   }

   /**
    * Determines if a value of any attribute in a given dependency list is overridden.
    * @param dep The dependency list.
    * @param metaclassSet The set to test, or null for all classes.
    * @return True if the value is overridden by a member of the set.
    */
   private static boolean isValueOverridden(Pair dep, Set metaclassSet)
   {
      while (dep != null)
      {
         Object head = dep.getHead();

         if (head instanceof Pair)
         {
            if (isValueOverridden((Pair)head, metaclassSet))
            {
               return true;
            }
         }
         else if (((Attribute)head).isValueOverridden(metaclassSet))
         {
            return true;
         }

         dep = dep.getNext();
      }

      return false;
   }

   /**
    * @return Value expression taking into account polymorphic value dispatch.
    */
   public Object getDispatchedValue()
   {
      Object value = m_value;

      if (value != Undefined.VALUE)
      {
         for (int i = 0, n = getValueDeclaratorCount(); i < n; ++i)
         {
            Metaclass metaclass = getValueDeclarator(i);

            if (metaclass.getPersistenceRoot() == m_metaclass.getPersistenceRoot())
            {
               Object derivedValue = metaclass.getDerivedAttribute(this).getValue();

               if (derivedValue == Undefined.VALUE)
               {
                  return derivedValue;
               }

               value = Pair.list(Symbol.IF,
                  Pair.binary(Symbol.INSTANCE_P, THIS, metaclass.getSymbol()),
                     derivedValue, value);
            }
         }
      }

      return value;
   }

   /**
    * Verifies and normalizes the attribute order by specification.
    * @param metaclass The class relative to which the list is.
    * @param orderBy The order by specification.
    * @return The newly allocated order by list.
    * @throws MetadataException if the verification fails.
    */
   private Pair resolveOrderBy(Metaclass metaclass, Pair orderBy) throws MetadataException
   {
      try
      {
         boolean bAscending = true;
         Pair first = null;
         Pair last = null;

         for (boolean bFirst = true; orderBy != null; orderBy = orderBy.getNext(), bFirst = false)
         {
            orderBy = new Pair(orderBy.getHead(), orderBy.getTail());

            if (orderBy.getHead() instanceof Pair)
            {
               Pair head = (Pair)orderBy.getHead();

               if (head.getHead() instanceof Symbol)
               {
                  metaclass.getAttribute(head.getHead().toString());
               }
               else if (!(head.getHead() instanceof Pair))
               {
                  throw new ClassCastException();
               }

               if (!(head.getTail() instanceof Boolean))
               {
                  throw new ClassCastException();
               }
              
               if (bFirst)
               {
                  bAscending = ((Boolean)head.getTail()).booleanValue();
                  head.setTail(Boolean.TRUE);
               }
               else
               {
                  head.setTail(Boolean.valueOf((!((Boolean)head.getTail()).booleanValue() ^ bAscending)));
               }
            }
            else
            {
               Symbol sym = (Symbol)orderBy.getHead();

               if (sym == null)
               {
                  throw new ClassCastException();
               }

               metaclass.getAttribute(sym.getName());
               orderBy.setHead(new Pair(sym, Boolean.valueOf(bAscending)));
            }
           
            if (first == null)
            {
               first = last = orderBy;
            }
            else
            {
               last.setTail(orderBy);
               last = orderBy;
            }
         }

         return first;
      }
      catch (MetadataException e)
      {
         throw e;
      }
      catch (ClassCastException e)
      {
         throw new MetadataException("err.meta.attributeOrderBy",
            new Object[]{getName(), m_metaclass.getName()});
      }
   }

   /**
    * Sets the order by expression in a derived attribute, if it is not overridden.
    * @param orderBy The order by expression to set.
    */
   private void setDerivedOrderBy(Pair orderBy)
   {
      m_orderBy = orderBy;

      for (int nDerived = 0, nDerivedCount = m_metaclass.getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         Attribute attribute = m_metaclass.getDerived(nDerived).getDerivedAttribute(this);

         if (attribute.m_declarator == m_declarator)
         {
            attribute.setDerivedOrderBy(orderBy);
         }
      }
   }
  
   /**
    * Computes the maximum attribute data length.
    */
   public void setMaxLength()
   {
      verifyNotReadOnly();

      if (m_type == Primitive.STRING || m_type == Primitive.BINARY)
      {
         AttributeMapping attributeMapping = getPersistenceMapping();

         if (attributeMapping != null)
         {
            m_nMaxLength = attributeMapping.getMaxLength((Primitive)m_type);
         }
      }
   }

   /**
    * @see nexj.core.meta.MetadataObject#setProperties(nexj.core.meta.MetadataMarker)
    */
   public void setProperties(MetadataMarker marker)
   {
      m_metaclass.setProperties(marker);
      marker.setTypeName("Attribute");
      marker.setProperty("attribute", m_sName);
   }

   /**
    * Validates the enumeration associated with the attribute.
    * @see nexj.core.meta.MetadataObject#validate(ContextMetadata, ExceptionHolder)
    */
   public void validateEnumeration(ContextMetadata metadata)
   {
      if (m_bConstrained && m_enumeration == null)
      {
         throw new MetadataException("err.meta.constrainedWithoutEnumeration",
            new Object[]{getName(), m_metaclass.getName()});
      }

      if (m_enumeration != null)
      {
         if (m_type.isPrimitive())
         {
            Attribute attribute = m_enumeration.findAttribute(Metaclass.ENUMERATION_VALUE);

            if (attribute == null)
            {
               throw new MetadataException("err.meta.missingEnumerationValueAttribute",
                  new Object[]{m_enumeration.getName(), getName(), m_metaclass.getName()});
            }

            if (attribute.isStatic())
            {
               throw new MetadataException("err.meta.staticEnumerationValueAttribute",
                  new Object[]{m_enumeration.getName(), getName(), m_metaclass.getName()});
            }
         }
         else
         {
            if (!((Metaclass)m_type).isUpcast(m_enumeration))
            {
               throw new MetadataException("err.meta.enumerationType",
                  new Object[]{m_enumeration.getName(), m_type.getName(),
                     getName(), m_metaclass.getName()});
            }
         }
      }
   }

   /**
    * @see nexj.core.meta.MetadataObject#validate(ContextMetadata, ExceptionHolder)
    */
   public void validate(ContextMetadata metadata, ExceptionHolder warnings)
   {
      super.validate(metadata, warnings);
      validateEnumeration(metadata);
   }

   /**
    * Checks the attribute visibility and read access against a privilege set.
    * @param privilegeSet The privilege set containing the allowed privileges.
    * @throws SecurityViolationException if the visibility is not public or the access is denied.
    */
   public void checkReadAccess(PrivilegeSet privilegeSet) throws SecurityViolationException
   {
      if (m_nVisibility != Metaclass.PUBLIC)
      {
         throw new SecurityViolationException("err.rpc.attributeVisibility",
            new Object[]{getName(), m_metaclass.getName()});
      }

      if (m_readPrivilege != null && !privilegeSet.contains(m_readPrivilege))
      {
         throw new SecurityViolationException("err.rpc.attributeReadPrivilege",
            new Object[]{getName(), m_metaclass.getName(), m_readPrivilege.getName()});
      }
   }

   /**
    * @return True if the attribute is readable according to a privilege set.
    */
   public boolean isReadable(PrivilegeSet privilegeSet)
   {
      return m_nVisibility == Metaclass.PUBLIC &&
         (m_readPrivilege == null || privilegeSet.contains(m_readPrivilege) &&
         (m_type.isPrimitive() || ((Metaclass)m_type).isReadable(privilegeSet)));
   }

   /**
    * Checks the attribute visibility and update access against a privilege set.
    * @param privilegeSet The privilege set containing the allowed privileges.
    * @throws SecurityViolationException if the visibility is not public or the access is denied.
    */
   public void checkUpdateAccess(PrivilegeSet privilegeSet) throws SecurityViolationException
   {
      if (m_nVisibility != Metaclass.PUBLIC)
      {
         throw new SecurityViolationException("err.rpc.attributeVisibility",
            new Object[]{getName(), m_metaclass.getName()});
      }

      if (m_bReadOnly)
      {
         throw new SecurityViolationException("err.runtime.attributeReadOnlyAccess",
            new Object[]{getName(), m_metaclass.getName()});
      }

      if (m_updatePrivilege != null && !privilegeSet.contains(m_updatePrivilege))
      {
         throw new SecurityViolationException("err.rpc.attributeUpdatePrivilege",
            new Object[]{getName(), m_metaclass.getName(), m_updatePrivilege.getName()});
      }
   }

   /**
    * @return True if the attribute is updateable according to a privilege set.
    */
   public boolean isUpdateable(PrivilegeSet privilegeSet)
   {
      return !m_bReadOnly && (m_updatePrivilege == null || privilegeSet.contains(m_updatePrivilege));
   }
  
   /**
    * @see nexj.core.meta.ui.AttributeMeta#getAccessAttributeMeta()
    */
   public AttributeMeta getAccessAttributeMeta()
   {
      return m_accessAttribute;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getClassMeta()
    */
   public ClassMeta getClassMeta()
   {
      return m_metaclass;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getDeclaratorClassMeta()
    */
   public ClassMeta getDeclaratorClassMeta()
   {
      return m_declarator;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getEnumerationClassMeta()
    */
   public ClassMeta getEnumerationClassMeta()
   {
      return m_enumeration;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getReadPrivilegeOrdinal()
    */
   public int getReadPrivilegeOrdinal()
   {
      if (m_readPrivilege != null)
      {
         return m_readPrivilege.getOrdinal();
      }
     
      return -1;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getReadPrivilegeOrdinals()
    */
   public Holder getReadPrivilegeOrdinals()
   {
      Holder set = new HashHolder(2);

      if (m_readPrivilege != null && m_readPrivilege.getOrdinal() >= 0)
      {
         set.add(Primitive.createInteger(m_readPrivilege.getOrdinal()));
      }
     
      if (m_type instanceof ClassMeta)
      {
         if (((ClassMeta)m_type).getReadPrivilegeOrdinal() >= 0)
         {
            set.add(Primitive.createInteger(((ClassMeta)m_type).getReadPrivilegeOrdinal()));
         }
      }

      if (set.size() == 0)
      {
         return null;
      }
     
      return set;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getReverseAttributeMeta()
    */
   public AttributeMeta getReverseAttributeMeta()
   {
      return m_reverse;
   }

   /**
    * @see nexj.core.meta.ui.AttributeMeta#getUpdatePrivilegeOrdinal()
    */
   public int getUpdatePrivilegeOrdinal()
   {
      if (m_updatePrivilege != null)
      {
         return m_updatePrivilege.m_nOrdinal;
      }
     
      return -1;
   }

   /**
    * @see nexj.core.meta.MetadataObject#makeReadOnly()
    */
   public void makeReadOnly()
   {
      if (m_valueDeclaratorList instanceof ArrayList)
      {
         ((ArrayList)m_valueDeclaratorList).trimToSize();
      }

      super.makeReadOnly();
   }

   /**
    * @return The basic URL for this attribute.
    */
   public String getURL()
   {
      return "class:" + m_declarator.getName() + "." + m_sName;
   }

   /**
    * @see java.lang.Comparable#compareTo(java.lang.Object)
    */
   public int compareTo(Object obj)
   {
      if (!(obj instanceof Attribute))
      {
         return getClass().getName().compareTo(obj.getClass().getName());
      }

      int n = m_metaclass.compareTo(((Attribute)obj).m_metaclass);

      if (n == 0)
      {
         n = super.compareTo(obj);
      }

      return n;
   }

   /**
    * @see nexj.core.meta.MetadataObject#clone()
    */
   public Object clone()
   {
      Attribute attribute = (Attribute)super.clone();

      attribute.m_persistenceRoot = attribute;

      return attribute;
   }

   /**
    * Replaces an instance of BindMeta with an instance of SerializedForm during serialization.
    *
    * @return An instance of SerializedForm which contains all state information needed to deserialize the instance.
    * @throws ObjectStreamException
    */
   protected Object writeReplace() throws ObjectStreamException
   {
      return new SerializedForm(this);
   }
  
   /**
    * Creates an incompatible attribute exception.
    * @param metaclass The metaclass.
    * @param sErrCode The error string identifier.
    * @param argArray The error string arguments.
    */
   private MetadataValidationException createCompatibilityException(Metaclass metaclass, String sErrCode, Object[] argArray)
   {
      MetadataValidationException mve = metaclass.createCompatibilityException(sErrCode, argArray);
     
      mve.setTypeName("Attribute");
      mve.setProperty("attribute", getName());
     
      return mve;
   }

   /**
    * @see nexj.core.meta.Member#checkCompatibility(nexj.core.meta.Metaclass, nexj.core.util.ExceptionHolder)
    */
   public void checkCompatibility(Metaclass metaclass, ExceptionHolder eh)
   {
      Attribute attribute = metaclass.findAttribute(getName());
     
      if (attribute == null)
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.missingCompatibleAttribute",
            new Object[] {getName(), metaclass.getName()}));
      }
      else if (!attribute.isCompatible())
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"compatible", attribute.getName(), metaclass.getName()}));
      }
      else if (attribute.getVisibility() != getVisibility() && attribute.getVisibility() != Metaclass.PUBLIC)
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"visibility", attribute.getName(), metaclass.getName()}));
      }
      else if (attribute.isRequired() != isRequired())
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"required", attribute.getName(), metaclass.getName()}));
      }
      else if (attribute.isCollection() != isCollection())
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"collection", attribute.getName(), metaclass.getName()}));
      }
      else if (attribute.isStatic() != isStatic())
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"static", attribute.getName(), metaclass.getName()}));
      }
      else if (attribute.isReadOnly() != isReadOnly())
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"readOnly", attribute.getName(), metaclass.getName()}));
      }
      else if (attribute.getType().isPrimitive() != getType().isPrimitive() || !attribute.getType().getName().equals(getType().getName()))
      {
         eh.addException(createCompatibilityException(metaclass, "err.meta.incompatibleAttributeProperty",
            new Object[] {"type", attribute.getName(), metaclass.getName()}));
      }
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#getChild(java.lang.String)
    */
   public EndpointPart getChild(String sName)
   {
      if (m_type.isPrimitive())
      {
         throw new MetadataLookupException("err.meta.namedLookup",
            new Object[]{EndpointPart.class.getName(), sName});
      }

      return ((Metaclass)m_type).getChild(sName);
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#findChild(java.lang.String)
    */
   public EndpointPart findChild(String sName)
   {
      if (m_type.isPrimitive())
      {
         return null;
      }

      return ((Metaclass)m_type).findChild(sName);
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#getChildIterator()
    */
   public Iterator getChildIterator()
   {
      return ((Metaclass)m_type).getChildIterator();
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#isPrimitive()
    */
   public boolean isPrimitive()
   {
      return m_type.isPrimitive();
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#createObject()
    */
   public TransferObject createObject()
   {
      if (m_type.isPrimitive())
      {
         throw new IllegalStateException(getName());
      }

      return ((Metaclass)m_type).createObject();
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#getValue(nexj.core.util.PropertyMap, java.lang.Object)
    */
   public Object getValue(PropertyMap map, Object defValue)
   {
      if (map instanceof Accessor)
      {
         return map.getValue(getName());
      }

      return map.findValue(getName(), defValue);
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#setValue(nexj.core.util.PropertyMap, java.lang.Object)
    */
   public void setValue(PropertyMap map, Object value)
   {
      map.setValue(getName(), value);
   }

   // inner classes
  
   /**
    * Inner class used to represent a given Attribute instance in serialized form.
    */
   private static class SerializedForm implements Serializable
   {
      /**
       * Serial Id.
       */
      private static final long serialVersionUID = 597784290101879829L;

      /**
       * Name of the ClassMeta to which this attribute belongs.
       */
      private String m_sClassName;
     
      /**
       * Name of the attribute.
       */
      private String m_sAttribName;
     
      /**
       * Constructor for the serialized form of an Attribute instance.
       *
       * @param Attribute
       * @throws InvalidObjectException
       */
      private SerializedForm (Attribute attrib) throws InvalidObjectException
      {
         m_sClassName = attrib.getClassMeta().getName();
         m_sAttribName = attrib.getName();
      }
     
      /**
       * Resolves an Attribute instance given the serialized state information.
       *
       * @return Attribute instance representing the serialized information.
       * @throws ObjectStreamException
       */
      private Object readResolve() throws ObjectStreamException
      {
         try
         {
            ClassMeta classMeta = Repository.getMetadata().getClassMeta(m_sClassName);

            return classMeta.getAttributeMeta(m_sAttribName);
         }
         catch (Exception e)
         {
            throw new InvalidObjectException(e.getMessage());
         }
      }
   }
  
   /**
    * Interface implemented by dependency nodes.
    */
   public static interface InverseDependency extends Cloneable
   {
      /**
       * Sets the next sibling.
       * @param next The next sibling.
       */
      void setNext(InverseDependency next);

      /**
       * @return The next sibling.
       */
      InverseDependency getNext();

      /**
       * @return The dependent attribute.
       */
      Attribute getAttribute();
     
      /**
       * @return True if there is persistent leaf attribute.
       */
      boolean isPersistent();
     
      /**
       * @return True if the node has children, false if it is a leaf.
       */
      boolean isIndirect();
     
      /**
       * @return A shallow copy of the dependency.
       */
      Object clone();
   }
  
   /**
    * Inverse dependency single-linked list.
    */
   public static class InverseDependencyList
   {
      // attributes
     
      /**
       * True if there is persistent leaf attribute.
       */
      protected boolean m_bPersistent;
     
      // associations
     
      /**
       * The first inverse dependency child node.
       */
      protected InverseDependency m_dependency;
     
      // operations
     
      /**
       * Sets the persistence flag.
       */
      public void setPersistent()
      {
         m_bPersistent = true;
      }
     
      /**
       * @return True if there is persistent leaf dependency.
       */
      public boolean isPersistent()
      {
         return m_bPersistent;
      }

      /**
       * @return The first inverse dependency child node.
       */
      public InverseDependency getDependency()
      {
         return m_dependency;
      }

      /**
       * Adds a child dependency node.
       * @param dep The dependency to add.
       */
      public void addDependency(InverseDependency dep)
      {
         m_bPersistent |= dep.isPersistent();
         dep.setNext(m_dependency);
         m_dependency = dep;
      }

      /**
       * Appends a dependency list to this one.
       * @param src The source dependency list.
       */
      public void append(InverseDependencyList src)
      {
         for (InverseDependency dep = src.getDependency(); dep != null; dep = dep.getNext())
         {
            addDependency((InverseDependency)dep.clone());
         }
      }

      /**
       * Appends the list string representation to a string buffer.
       * @param buf The string buffer.
       */
      protected void append(StringBuffer buf)
      {
         if (m_bPersistent)
         {
            buf.append("persistent:");
         }
        
         for (InverseDependency dep = m_dependency; dep != null; dep = dep.getNext())
         {
            if (m_bPersistent || dep != m_dependency)
            {
               buf.append(' ');
            }
           
            buf.append(dep);
         }
      }

      /**
       * @see java.lang.Object#toString()
       */
      public String toString()
      {
         StringBuffer buf = new StringBuffer(32);
        
         append(buf);
        
         return buf.toString();
      }
   }
  
   /**
    * Direct (leaf) inverse dependency.
    */
   public final static class DirectInverseDependency implements InverseDependency
   {
      // associations
     
      /**
       * The dependent attribute.
       */
      protected Attribute m_attribute;
     
      /**
       * The next dependency.
       */
      protected InverseDependency m_next;
     
      // constructors
     
      /**
       * Constructs the dependency.
       * @param attribute The dependent attribute.
       */
      public DirectInverseDependency(Attribute attribute)
      {
         m_attribute = attribute;
      }

      // operations

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#getAttribute()
       */
      public Attribute getAttribute()
      {
         return m_attribute;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#setNext(nexj.core.meta.Attribute.InverseDependency)
       */
      public void setNext(InverseDependency next)
      {
         m_next = next;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#getNext()
       */
      public InverseDependency getNext()
      {
         return m_next;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#isIndirect()
       */
      public boolean isIndirect()
      {
         return false;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#isPersistent()
       */
      public boolean isPersistent()
      {
         return m_attribute.isPersistent();
      }

      /**
       * @see java.lang.Object#clone()
       */
      public Object clone()
      {
         try
         {
            return super.clone();
         }
         catch (CloneNotSupportedException e)
         {
            return null;
         }
      }

      /**
       * @see java.lang.Object#toString()
       */
      public String toString()
      {
         return m_attribute.getName();
      }
   }
  
   public final static class IndirectInverseDependency
      extends InverseDependencyList implements InverseDependency
   {
      // associations
     
      /**
       * The dependent attribute.
       */
      protected Attribute m_attribute;
     
      /**
       * The next dependency.
       */
      protected InverseDependency m_next;
     
      // constructors
     
      /**
       * Constructs the dependency.
       * @param attribute The dependent attribute.
       */
      public IndirectInverseDependency(Attribute attribute)
      {
         m_attribute = attribute;
      }

      // operations

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#getAttribute()
       */
      public Attribute getAttribute()
      {
         return m_attribute;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#setNext(nexj.core.meta.Attribute.InverseDependency)
       */
      public void setNext(InverseDependency next)
      {
         m_next = next;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#getNext()
       */
      public InverseDependency getNext()
      {
         return m_next;
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependency#isIndirect()
       */
      public boolean isIndirect()
      {
         return true;
      }

      /**
       * @see java.lang.Object#clone()
       */
      public Object clone()
      {
         try
         {
            return super.clone();
         }
         catch (CloneNotSupportedException e)
         {
            return null;
         }
      }

      /**
       * @see nexj.core.meta.Attribute.InverseDependencyList#toString()
       */
      public String toString()
      {
         StringBuffer buf = new StringBuffer(32);

         buf.append(m_attribute.getName());
         buf.append("->(");
         append(buf);
         buf.append(')');

         return buf.toString();
      }
   }
}
TOP

Related Classes of nexj.core.meta.Attribute$DirectInverseDependency

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.