Package nexj.core.meta

Source Code of nexj.core.meta.Metaclass$Local

// 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.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import nexj.core.meta.integration.Transformation;
import nexj.core.meta.integration.TransformationEndpoint;
import nexj.core.meta.integration.EndpointPart;
import nexj.core.meta.persistence.AttributeMapping;
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.Context;
import nexj.core.runtime.SecurityViolationException;
import nexj.core.runtime.ThreadContextHolder;
import nexj.core.runtime.ValidationException;
import nexj.core.scripting.Compiler;
import nexj.core.scripting.Function;
import nexj.core.scripting.GlobalEnvironment;
import nexj.core.scripting.Machine;
import nexj.core.scripting.Macro;
import nexj.core.scripting.Pair;
import nexj.core.scripting.ScriptingException;
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.HashTab;
import nexj.core.util.Holder;
import nexj.core.util.IdentityHashTab;
import nexj.core.util.Invalid;
import nexj.core.util.Logger;
import nexj.core.util.LoggerHolder;
import nexj.core.util.Lookup;
import nexj.core.util.ObjUtil;
import nexj.core.util.PropertyMap;
import nexj.core.util.StringUtil;
import nexj.core.util.TextPosition;
import nexj.core.util.UncheckedException;
import nexj.core.util.Undefined;

/**
* The object system metaclass object.
*/
public class Metaclass extends ClassMeta implements MetadataResource, Accessor,
   Function, LoggerHolder, Captioned, Documented, Pointcut, TransformationEndpoint
{
   // constants

   // visibility constants

   /**
    * Public visibility - the item can be accessed through RPC.
    */
   public final static byte PUBLIC = 0;

   /**
    * Protected visibility - the item can be accessed only in-process.
    */
   public final static byte PROTECTED = 1;

   // Enumeration attributes

   /**
    * The name of the enumeration value attribute symbol.
    */
   public final static Symbol ENUMERATION_VALUE = Symbol.VALUE;

   /**
    * The name of the enumeration caption attribute symbol.
    */
   public final static Symbol ENUMERATION_CAPTION = Symbol.CAPTION;

   /**
    * The name of the enumeration display order attribute.
    */
   public final static Symbol ENUMERATION_DISPLAY_ORDER = Symbol.DISPLAY_ORDER;

   // other constants

   /**
    * Prefix prepended to the class name to obtain a log category.
    */
   public final static String LOG_CATEGORY_PREFIX = Metaclass.class.getName() + '.';

   /**
    * Global variable with the currently compiled class for macros.
    */
   protected final static Symbol SYS_CURRENT_CLASS = Symbol.define("sys:current-class");

   /**
    * Symbol used in in combined validation expressions.
    */
   protected final static Symbol VALIDATION_ARG = Symbol.define("#b");

   /**
    * Expressions that are generally true.
    */
   protected final static Object[] GENERALLY_TRUE_EXPRESSIONS = new Object[]
   {
      Pair.list(Pair.list(Symbol.INVOCATION_CONTEXT), Pair.quote(Symbol.define("partitioned"))),
      Boolean.TRUE,
   };

   /**
    * A comparator for sorting the attributes by ordinal number.
    */
   protected final static Comparator ATTRIBUTE_ORDINAL_COMPARATOR = new Comparator()
   {
      public int compare(Object o1, Object o2)
      {
         return ((Attribute)o1).getOrdinal() - ((Attribute)o2).getOrdinal();
      }
   };

   // attributes

   /**
    * The class visibility.
    */
   protected byte m_nVisibility;

   /**
    * The create transaction mode, one of the Event.TX_* constants.
    */
   protected byte m_nCreateTransactionMode;

   /**
    * The update transaction mode, one of the Event.TX_* constants.
    */
   protected byte m_nUpdateTransactionMode;

   /**
    * True if the class is a pointcut.
    */
   protected boolean m_bPointcut;

   /**
    * The direct aspect count - direct aspects have indexes [0..nCount-1].
    */
   protected int m_nDirectAspectCount;

   /**
    * The class caption string id.
    */
   protected String m_sCaption;

   /**
    * The metadata object description.
    */
   protected String m_sDescription;

   /**
    * The metadata resource name.
    */
   protected String m_sResourceName;

   // associations

   /**
    * The class symbol.
    */
   protected Symbol m_symbol;

   /**
    * The create privilege.
    */
   protected PrimitivePrivilege m_createPrivilege;

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

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

   /**
    * The delete privilege.
    */
   protected PrimitivePrivilege m_deletePrivilege;

   /**
    * The read access attribute.
    */
   protected Attribute m_readAccessAttribute;

   /**
    * The update access attribute.
    */
   protected Attribute m_updateAccessAttribute;

   /**
    * The delete access attribute.
    */
   protected Attribute m_deleteAccessAttribute;

   /**
    * The class event handler collection.
    */
   protected List m_eventList = new ArrayList(4); // of type Event[]

   /**
    * The map of the class attributes (name to attribute).
    */
   protected Lookup m_attributeMap = new HashTab(8); // of type Attribute[String]

   /**
    * The instance attribute list, in their order of declaration.
    */
   protected List m_instanceAttributeList = new ArrayList(8); // of type Attribute[]

   /**
    * The static attribute list, in their order of declaration.
    */
   protected List m_staticAttributeList; // of type Attribute[]

   /**
    * The array of initialized static attributes, sorted by
    * initialization priority. Can be null.
    */
   protected Attribute[] m_initializedStaticAttributeArray;

   /**
    * The derived class collection.
    */
   protected List m_derivedList; // of type Metaclass[]

   /**
    * The base class.
    */
   protected Metaclass m_base;

   /**
    * The topmost class with compatible persistence mapping,
    * such that all the base classes between it an this class
    * are with a compatible persistence mapping.
    */
   protected Metaclass m_persistenceRoot = this;

   /**
    * The persistence alias collection.
    * These are classes with compatible persistence mapping and
    * type code attribute which is used to resolve a different hierarchy.
    */
   protected List m_persistenceAliasList; // of type Metaclass[]

   /**
    * The where expression. Can be null.
    */
   protected Object m_where;

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

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

   /**
    * The validation function text position map
    */
   protected Lookup m_textPosMap;

   /**
    * The attribute containing the instance name.
    */
   protected Attribute m_nameAttribute;

   /**
    * The persistence mapping.
    * Null means that the class is not persisted.
    */
   protected PersistenceMapping m_persistenceMapping;

   /**
    * The selector map: Selector[String].
    */
   protected Lookup m_selectorMap = new HashTab(16);

   /**
    * The pointcut helper.
    */
   protected ClassPointcutHelper m_pointcutHelper;

   /**
    * The root metadata object.
    */
   protected Metadata m_metadata;

   /**
    * The class logger.
    */
   protected Logger m_logger;

   // constructors

   /**
    * Creates a class with a given name.
    * @param sName The class name.
    */
   public Metaclass(String sName)
   {
      super(sName);
      m_symbol = Symbol.define(m_sName);
      m_sCaption = m_sName;
   }

   // operations

   /**
    * @return The class symbol.
    */
   public final Symbol getSymbol()
   {
      return m_symbol;
   }

   /**
    * @see nexj.core.meta.MetadataResource#setResourceName(java.lang.String)
    */
   public void setResourceName(String sName)
   {
      verifyNotReadOnly();
      m_sResourceName = sName;
   }

   /**
    * @see nexj.core.meta.MetadataResource#getResourceName()
    */
   public String getResourceName()
   {
      return m_sResourceName;
   }

   /**
    * @return false
    * @see nexj.core.meta.Type#isPrimitive()
    */
   public final boolean isPrimitive()
   {
      return false;
   }

   /**
    * @see nexj.core.meta.Type#getBaseType()
    */
   public final Type getBaseType()
   {
      return m_base;
   }

   /**
    * @see nexj.core.meta.Type#isUpcast(nexj.core.meta.Type)
    */
   public final boolean isUpcast(Type type)
   {
      if (type instanceof Metaclass)
      {
         return isUpcast((Metaclass)type);
      }

      return super.isUpcast(type);
   }

   /**
    * @see nexj.core.meta.Type#convert(java.lang.Object)
    */
   public final Object convert(Object value) throws TypeConversionException
   {
      if (value == null)
      {
         return null;
      }

      if (value instanceof Accessor &&
         isUpcast(((Accessor)value).getMetaclass()))
      {
         return value;
      }

      if (value instanceof Typed &&
         isUpcast(((Typed)value).getType()))
      {
         return value;
      }

      throw new TypeConversionException(this);
   }

   /**
    * Sets the class visibility.
    * @param nVisibility The class visibility to set.
    */
   public final void setVisibility(byte nVisibility)
   {
      verifyNotReadOnly();
      m_nVisibility = nVisibility;
   }

   /**
    * @return The class visibility.
    */
   public final byte getVisibility()
   {
      return m_nVisibility;
   }

   /**
    * Sets the attribute containing the instance name.
    * @param sAttribute The name of the attribute containing the instance name to set.
    */
   public final void setNameAttribute(String sAttribute)
   {
      setNameAttribute(
         (sAttribute == null || sAttribute.length() == 0) ?
            null : getAttribute(sAttribute));
   }

   /**
    * Sets the attribute containing the instance name.
    * @param attribute The attribute containing the instance name to set.
    */
   public final void setNameAttribute(Attribute attribute)
   {
      verifyNotReadOnly();

      if (attribute != null)
      {
         if (attribute.getType() != Primitive.STRING)
         {
            throw new MetadataException("err.meta.nameAttributeType",
               new Object[]{attribute.getName(), getName()});
         }

         if (attribute.isStatic())
         {
            throw new MetadataException("err.meta.staticNameAttribute",
               new Object[]{attribute.getName(), getName()});
         }
      }

      m_nameAttribute = attribute;
   }

   /**
    * @return The attribute containing the instance name.
    */
   public final Attribute getNameAttribute()
   {
      return m_nameAttribute;
   }

   /**
    * Sets the class caption string id.
    * @param sCaption The class caption string id to set.
    */
   public final void setCaption(String sCaption)
   {
      verifyNotReadOnly();
      m_sCaption = sCaption;
   }

   /**
    * @return The class caption string id.
    */
   public final String getCaption()
   {
      return m_sCaption;
   }

   /**
    * @see nexj.core.meta.Documented#setDescription(java.lang.String)
    */
   public final void setDescription(String sDescription)
   {
      verifyNotReadOnly();

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

      m_sDescription = sDescription;
   }

   /**
    * @see nexj.core.meta.Documented#getDescription()
    */
   public final String getDescription()
   {
      return m_sDescription;
   }

   /**
    * Sets the create privilege.
    * @param createPrivilege The create privilege to set.
    */
   public final void setCreatePrivilege(PrimitivePrivilege createPrivilege)
   {
      verifyNotReadOnly();
      m_createPrivilege = createPrivilege;
   }

   /**
    * @return The create privilege.
    */
   public final PrimitivePrivilege getCreatePrivilege()
   {
      return m_createPrivilege;
   }

   /**
    * Sets the read privilege.
    * @param readPrivilege The read privilege to set.
    */
   public final void setReadPrivilege(PrimitivePrivilege readPrivilege)
   {
      verifyNotReadOnly();
      m_readPrivilege = readPrivilege;
   }

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

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

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

   /**
    * Sets the delete privilege.
    * @param deletePrivilege The delete privilege to set.
    */
   public final void setDeletePrivilege(PrimitivePrivilege deletePrivilege)
   {
      verifyNotReadOnly();
      m_deletePrivilege = deletePrivilege;
   }

   /**
    * @return The delete privilege.
    */
   public final PrimitivePrivilege getDeletePrivilege()
   {
      return m_deletePrivilege;
   }

   /**
    * Sets the read access attribute.
    * @param readAccessAttribute The read access attribute to set.
    */
   public final void setReadAccessAttribute(Attribute readAccessAttribute)
   {
      verifyNotReadOnly();
      m_readAccessAttribute = readAccessAttribute;
   }

   /**
    * @return The read access attribute.
    */
   public final Attribute getReadAccessAttribute()
   {
      return m_readAccessAttribute;
   }

   /**
    * Sets the update access attribute.
    * @param updateAccessAttribute The update access attribute to set.
    */
   public final void setUpdateAccessAttribute(Attribute updateAccessAttribute)
   {
      verifyNotReadOnly();
      m_updateAccessAttribute = updateAccessAttribute;
   }

   /**
    * Sets the delete access attribute.
    * @param deleteAccessAttribute The delete access attribute to set.
    */
   public final void setDeleteAccessAttribute(Attribute deleteAccessAttribute)
   {
      verifyNotReadOnly();
      m_deleteAccessAttribute = deleteAccessAttribute;
   }

   /**
    * @return The delete access attribute.
    */
   public final Attribute getDeleteAccessAttribute()
   {
      return m_deleteAccessAttribute;
   }

   /**
    * Sets the create transaction mode.
    * @param nCreateTransactionMode The create transaction mode to set, one of the Event.TX_* constants.
    */
   public final void setCreateTransactionMode(byte nCreateTransactionMode)
   {
      verifyNotReadOnly();
      m_nCreateTransactionMode = nCreateTransactionMode;
   }

   /**
    * @return The create transaction mode, one of the Event.TX_* constants.
    */
   public final byte getCreateTransactionMode()
   {
      return m_nCreateTransactionMode;
   }

   /**
    * @return The update access attribute.
    */
   public final Attribute getUpdateAccessAttribute()
   {
      return m_updateAccessAttribute;
   }

   /**
    * Sets the update transaction mode.
    * @param nUpdateTransactionMode The update transaction mode to set, one of the Event.TX_* constants.
    */
   public final void setUpdateTransactionMode(byte nUpdateTransactionMode)
   {
      verifyNotReadOnly();
      m_nUpdateTransactionMode = nUpdateTransactionMode;
   }

   /**
    * @return The update transaction mode, one of the Event.TX_* constants.
    */
   public final byte getUpdateTransactionMode()
   {
      return m_nUpdateTransactionMode;
   }

   /**
    * Determines if a class can be upcast to obtain this class.
    * @param metaclass The class to upcast.
    * @return True if this class can be obtained by upcasting metaclass.
    */
   public final boolean isUpcast(Metaclass metaclass)
   {
      while (metaclass != null)
      {
         if (metaclass == this)
         {
            return true;
         }

         metaclass = metaclass.m_base;
      }

      return false;
   }

   /**
    * Adds a new event handler to the class.
    * @param event The event handler to add.
    */
   public final void addEvent(Event event)
   {
      verifyNotReadOnly();
      m_eventList.add(event);
      event.setMetaclass(this);

      Selector selector = addSelector(event.getName());

      selector.addMember(event, event.getArgumentCount(), event.isVarArg());
      event.setSelector(selector);
   }

   /**
    * Gets a event handler by ordinal number.
    * @param nOrdinal The event handler ordinal number (0-based).
    * @return The event handler object.
    */
   public final Event getEvent(int nOrdinal)
   {
      return (Event)m_eventList.get(nOrdinal);
   }

   /**
    * @return The event handler count.
    */
   public final int getEventCount()
   {
      return m_eventList.size();
   }

   /**
    * @return An iterator for the contained event handler objects.
    */
   public final Iterator getEventIterator()
   {
      return m_eventList.iterator();
   }

   /**
    * Adds a new attribute to the class.
    * @param attribute The attribute to add.
    * @throws MetadataException if an attribute
    * with the same name already exists.
    */
   public final void addAttribute(Attribute attribute)
   {
      verifyNotReadOnly();

      Object oldAttribute = m_attributeMap.put(attribute.getName(), attribute);

      if (oldAttribute != null)
      {
         m_attributeMap.put(attribute.getName(), oldAttribute);

         throw new MetadataException("err.meta.attributeDup", new Object[] {attribute.getName(), getName()});
      }

      attribute.setMetaclass(this);

      if (attribute.isStatic())
      {
         if (m_staticAttributeList == null)
         {
            m_staticAttributeList = new ArrayList(4);
         }

         m_staticAttributeList.add(attribute);
      }
      else
      {
         m_instanceAttributeList.add(attribute);
      }

      Selector selector = addSelector(attribute.getName());

      selector.addMember(attribute, 0, false);
      selector.addMember(attribute, 1, false);
   }

   /**
    * Gets an attribute by name.
    * @param sName The attribute name.
    * @return The attribute object.
    * @throws MetadataLookupException if the attribute does not exist.
    */
   public final Attribute getAttribute(String sName)
   {
      Attribute attribute = (Attribute)m_attributeMap.get(sName);

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

      throw new MetadataLookupException("err.meta.attributeLookup", sName, this);
   }

   /**
    * Gets an attribute by symbol.
    * @param sym The attribute symbol.
    * @return The attribute object.
    * @throws MetadataLookupException if the attribute does not exist.
    */
   public final Attribute getAttribute(Symbol sym)
   {
      return getAttribute(sym.getName());
   }

   /**
    * Finds an attribute by name.
    * @param sName The attribute name.
    * @return The attribute object, or null if not found.
    */
   public final Attribute findAttribute(String sName)
   {
      return (Attribute)m_attributeMap.get(sName);
   }

   /**
    * Finds an attribute by symbol.
    * @param sym The attribute symbol.
    * @return The attribute object, or null if not found.
    */
   public final Attribute findAttribute(Symbol sym)
   {
      return findAttribute(sym.getName());
   }

   /**
    * Gets a derived attribute given a base attribute.
    * @param attribute The base attribute for which to get the derived one.
    * @return The derived attribute.
    */
   public final Attribute getDerivedAttribute(Attribute attribute)
   {
      if (attribute == null)
      {
         return null;
      }

      List list = (attribute.isStatic()) ? m_staticAttributeList : m_instanceAttributeList;

      if (attribute.getOrdinal() >= list.size())
      {
         return attribute;
      }

      return (Attribute)list.get(attribute.getOrdinal());
   }

   /**
    * Gets an instance attribute by ordinal number.
    * @param nOrdinal The instance attribute ordinal number.
    * @return The attribute object.
    */
   public final Attribute getInstanceAttribute(int nOrdinal)
   {
      return (Attribute)m_instanceAttributeList.get(nOrdinal);
   }

   /**
    * @return The instance attribute count.
    */
   public final int getInstanceAttributeCount()
   {
      return m_instanceAttributeList.size();
   }

   /**
    * @return An iterator for the instance attributes.
    */
   public final Iterator getInstanceAttributeIterator()
   {
      if (m_instanceAttributeList == null)
      {
         return EmptyIterator.getInstance();
      }

      return m_instanceAttributeList.iterator();
   }

   /**
    * Gets an instance attribute by ordinal number.
    * @param nOrdinal The instance attribute ordinal number.
    * @return The attribute object.
    */
   public final Attribute getStaticAttribute(int nOrdinal)
   {
      return (Attribute)m_staticAttributeList.get(nOrdinal);
   }

   /**
    * @return The instance attribute count.
    */
   public final int getStaticAttributeCount()
   {
      if (m_staticAttributeList == null)
      {
         return 0;
      }

      return m_staticAttributeList.size();
   }

   /**
    * @return An iterator for the static attributes.
    */
   public final Iterator getStaticAttributeIterator()
   {
      if (m_staticAttributeList == null)
      {
         return EmptyIterator.getInstance();
      }

      return m_staticAttributeList.iterator();
   }

   /**
    * Gets an initialized static attribute by ordinal number.
    * @param nOrdinal The inzitialized static attribute ordinal number.
    * @return The attribute object.
    */
   public final Attribute getInitializedStaticAttribute(int nOrdinal)
   {
      return m_initializedStaticAttributeArray[nOrdinal];
   }

   /**
    * @return The initialized static attribute count.
    */
   public final int getInitializedStaticAttributeCount()
   {
      if (m_initializedStaticAttributeArray == null)
      {
         return 0;
      }

      return m_initializedStaticAttributeArray.length;
   }

   /**
    * @return The attribute count.
    */
   public final int getAttributeCount()
   {
      return m_attributeMap.size();
   }

   /**
    * @return An iterator for the contained attribute objects.
    */
   public final Iterator getAttributeIterator()
   {
      return m_attributeMap.valueIterator();
   }

   /**
    * Adds a new derived class to the class.
    * @param derived The derived class to add.
    */
   public final void addDerived(Metaclass derived)
   {
      verifyNotReadOnly();

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

      m_derivedList.add(derived);
      derived.setBase(this);
   }

   /**
    * Gets a derived class by ordinal number.
    * @param nOrdinal The derived class ordinal number (0-based).
    * @return The derived class object.
    */
   public final Metaclass getDerived(int nOrdinal)
   {
      return (Metaclass)m_derivedList.get(nOrdinal);
   }

   /**
    * @return The derived class count.
    */
   public final int getDerivedCount()
   {
      if (m_derivedList == null)
      {
         return 0;
      }

      return m_derivedList.size();
   }

   /**
    * @return An iterator for the contained derived class objects.
    */
   public final Iterator getDerivedIterator()
   {
      if (m_derivedList == null)
      {
         return EmptyIterator.getInstance();
      }

      return m_derivedList.iterator();
   }

   /**
    * Sets the base class.
    * @param base The base class to set.
    */
   public final void setBase(Metaclass base)
   {
      verifyNotReadOnly();
      m_base = base;
   }

   /**
    * @return The base class.
    */
   public final Metaclass getBase()
   {
      return m_base;
   }

   /**
    * Sets the where expression.
    * @param where The where expression to set.
    */
   public final void setWhere(Object where)
   {
      verifyNotReadOnly();
      m_where = where;
   }

   /**
    * @return The where expression.
    */
   public final Object getWhere()
   {
      return m_where;
   }

   /**
    * Sets the validation expression.
    * @param validation The validation expression to set.
    */
   public final void setValidation(Object validation)
   {
      verifyNotReadOnly();
      m_validation = validation;
   }

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

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

   /**
    * @return The validation function text position map
    */
   public final Lookup getTextPositionMap()
   {
      return m_textPosMap;
   }

   /**
    * Sets the validation function text position map
    * @param textPosMap The validation function text position map to set.
    */
   public final void setTextPositionMap(Lookup textPosMap)
   {
      verifyNotReadOnly();
      m_textPosMap = textPosMap;
   }

   /**
    * Sets the root metadata object.
    * @param metadata The root metadata object to set.
    */
   public final void setMetadata(Metadata metadata)
   {
      verifyNotReadOnly();
      m_metadata = metadata;
   }

   /**
    * @return The root metadata object.
    */
   public final Metadata getMetadata()
   {
      return m_metadata;
   }

   /**
    * Sets the persistence mapping.
    * @param persistenceMapping The persistence mapping to set. Can be null.
    */
   public final void setPersistenceMapping(PersistenceMapping persistenceMapping)
   {
      verifyNotReadOnly();
      m_persistenceMapping = persistenceMapping;
   }

   /**
    * @return The persistence mapping.
    */
   public final PersistenceMapping getPersistenceMapping()
   {
      return m_persistenceMapping;
   }

   /**
    * Visits the persistence mapping.
    * @param visitor The persistence mapping visitor.
    */
   public final void visit(PersistenceMapping.Visitor visitor)
   {
      if (m_persistenceMapping != null)
      {
         m_persistenceMapping.visit(visitor);
      }
   }

   /**
    * @return The topmost class with compatible persistence mapping.
    */
   public final Metaclass getPersistenceRoot()
   {
      return m_persistenceRoot;
   }

   /**
    * Adds a new persistence alias to the class.
    * @param persistenceAlias The persistence alias to add.
    */
   public void addPersistenceAlias(Metaclass persistenceAlias)
   {
      verifyNotReadOnly();

      if (m_persistenceAliasList == null)
      {
         m_persistenceAliasList = new ArrayList(2);
      }

      if (!m_persistenceAliasList.contains(persistenceAlias))
      {
         m_persistenceAliasList.add(persistenceAlias);

         for (int i = 0, n = m_persistenceAliasList.size() - 1; i < n; ++i)
         {
            getPersistenceAlias(i).addPersistenceAlias(persistenceAlias);
         }

         persistenceAlias.addPersistenceAlias(this);
      }
   }

   /**
    * Gets a persistence alias by ordinal number.
    * @param nOrdinal The persistence alias ordinal number (0-based).
    * @return The persistence alias object.
    */
   public Metaclass getPersistenceAlias(int nOrdinal)
   {
      return (Metaclass)m_persistenceAliasList.get(nOrdinal);
   }

   /**
    * @return The persistence alias count.
    */
   public int getPersistenceAliasCount()
   {
      if (m_persistenceAliasList == null)
      {
         return 0;
      }

      return m_persistenceAliasList.size();
   }

   /**
    * @return An iterator for the contained persistence alias objects.
    */
   public Iterator getPersistenceAliasIterator()
   {
      if (m_persistenceAliasList == null)
      {
         return EmptyIterator.getInstance();
      }

      return m_persistenceAliasList.iterator();
   }

   /**
    * Adds a selector to the class.
    * @param sName The selector name.
    * @return The added selector (or an old selector with the same name).
    */
   private final Selector addSelector(String sName)
   {
      Selector selector = (Selector)m_selectorMap.get(sName);

      if (selector == null)
      {
         selector = new Selector(sName);
         selector.setMetaclass(this);
         m_selectorMap.put(sName, selector);
      }

      return selector;
   }

   /**
    * Gets a selector by name.
    * @param sName The selector name.
    * @return The selector object.
    * @throws MetadataLookupException if the selector does not exist.
    */
   public final Selector getSelector(String sName)
   {
      Selector selector = (Selector)m_selectorMap.get(sName);

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

      throw new MetadataLookupException("err.meta.selectorLookup", sName, this);
   }

   /**
    * Gets a selector by symbol.
    * @param sym The selector symbol.
    * @return The selector object.
    * @throws MetadataLookupException if the selector does not exist.
    */
   public final Selector getSelector(Symbol sym)
   {
      return getSelector(sym.getName());
   }

   /**
    * Finds a selector by name.
    * @param sName The selector name.
    * @return The selector object, or null if not found.
    */
   public final Selector findSelector(String sName)
   {
      return (Selector)m_selectorMap.get(sName);
   }

   /**
    * Finds a selector by symbol.
    * @param sym The selector symbol.
    * @return The selector object, or null if not found.
    */
   public final Selector findSelector(Symbol sym)
   {
      return findSelector(sym.getName());
   }

   /**
    * @return The selector count.
    */
   public final int getSelectorCount()
   {
      return m_selectorMap.size();
   }

   /**
    * @return An iterator for the contained selector objects.
    */
   public final Iterator getSelectorIterator()
   {
      return m_selectorMap.valueIterator();
   }

   /**
    * @return The class logger.
    */
   public final Logger getLogger()
   {
      return m_logger;
   }

   /**
    * Template method to set the next action by name.
    * @param action The action on which to set the next one.
    * @param sName The next action name.
    */
   public void setNextAction(Action action, String sName)
   {
      action.setNextAction(action.getEvent().getAction(sName));
   }

   /**
    * Checks if there is a cycle in the inheritance hierarchy.
    * @throws MetadataException if a cycle is found and this class is in it.
    */
   public final void checkInheritance()
   {
      Metaclass base = this;
      Metaclass base2 = base;
      boolean bInLoop = false;

      for (;;)
      {
         base = base.getBase();
         base2 = base2.getBase();

         if (base2 == null)
         {
            return;
         }

         if (base2 == this)
         {
            bInLoop = true;
         }

         base2 = base2.getBase();

         if (base2 == null)
         {
            return;
         }

         if (base2 == this)
         {
            bInLoop = true;
         }

         if (base2 == base)
         {
            if (bInLoop)
            {
               throw new MetadataException("err.meta.inheritanceCycle", new Object[]{getName()});
            }

            return;
         }
      }
   }

   /**
    * Computes and verifies the inherited attributes,
    * events and actions for this class and its subclasses.
    * @throws MetadataException if inconsistencies are found.
    */
   public final void resolveInheritance()
   {
      ExceptionHolder eh = resolveInheritance(null);

      if (eh != null)
      {
         throw (UncheckedException)eh;
      }
   }

   /**
    * Computes and verifies the inherited attributes,
    * events and actions for this class and its subclasses.
    * @param eh The exception holder where to add the exceptions or null to create a new one.
    * @return The exception holder.
    */
   protected ExceptionHolder resolveInheritance(ExceptionHolder eh)
   {
      verifyNotReadOnly();

      // Assign ordinals to the attributes and sort them

      sortAttributes(m_instanceAttributeList);

      if (m_staticAttributeList != null)
      {
         sortAttributes(m_staticAttributeList);
      }

      // Compute the root declarators

      if (m_staticAttributeList != null)
      {
         for (int i = 0, nCount = m_staticAttributeList.size(); i < nCount; ++i)
         {
            Attribute derivedAttr = (Attribute)m_staticAttributeList.get(i);
            Attribute baseAttr = (m_base == null) ? null : m_base.findAttribute(derivedAttr.getName());

            derivedAttr.setRootDeclarator((baseAttr == null) ? this : baseAttr.getRootDeclarator());
         }
      }

      if (m_instanceAttributeList != null)
      {
         for (int i = 0, nCount = m_instanceAttributeList.size(); i < nCount; ++i)
         {
            Attribute derivedAttr = (Attribute)m_instanceAttributeList.get(i);
            Attribute baseAttr = (m_base == null) ? null : m_base.findAttribute(derivedAttr.getName());

            derivedAttr.setRootDeclarator((baseAttr == null) ? this : baseAttr.getRootDeclarator());
         }
      }

      if (m_eventList != null)
      {
         for (int i = 0, nCount = m_eventList.size(); i < nCount; ++i)
         {
            Event derivedEvent = (Event)m_eventList.get(i);
            Event baseEvent = (m_base == null) ? null : m_base.findEvent(derivedEvent.getName(), derivedEvent.getArgumentCount());

            derivedEvent.setRootDeclarator((baseEvent == null) ? this : baseEvent.getRootDeclarator());
         }
      }

      // Resolve the derived classes

      for (int nDerived = 0, nDerivedCount = getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         Metaclass derivedClass = getDerived(nDerived);

         eh = derivedClass.applyAspects(eh);
         eh = deriveMembers(derivedClass, eh);
         eh = derivedClass.resolveInheritance(eh);
      }

      return eh;
   }

   /**
    * Applies the aspects to this class.
    * @param eh The exception holder where to add the exceptions or null to create a new one.
    * @return The exception holder.
    */
   protected ExceptionHolder applyAspects(ExceptionHolder eh)
   {
      // Add matching aspects

      if (isPointcut())
      {
         for (Iterator itr = m_metadata.getClassAspectIterator(); itr.hasNext();)
         {
            ClassAspect aspect = (ClassAspect)itr.next();

            aspect.addTo(this);
         }
      }

      m_nDirectAspectCount = getAspectCount();

      // Apply aspects

      for (int i = 0, n = getAspectCount(); i < n; ++i)
      {
         ClassAspect aspect = (ClassAspect)getAspect(i);

         AspectManager.logApply(aspect, this);
         eh = aspect.deriveMembers(this, eh);
      }

      return eh;
   }

   /**
    * Derives the members from this class.
    * @param derivedClass The derived class.
    * @param eh The exception holder where to add the exceptions or null to create a new one.
    * @return The exception holder.
    */
   protected ExceptionHolder deriveMembers(Metaclass derivedClass, ExceptionHolder eh)
   {
      // Derive the aspects

      for (int i = 0, n = getAspectCount(); i < n; ++i)
      {
         Aspect aspect = getAspect(i);

         if (derivedClass.findAspectOverride(aspect) < 0)
         {
            derivedClass.addAspectOverride(aspect, true);
         }
      }

      // Derive the attributes

      for (int nStatic = 0; nStatic <= 1; ++nStatic)
      {
         boolean bStatic = (nStatic != 0);
         List attributeList = (bStatic) ? m_staticAttributeList : m_instanceAttributeList;

         if (attributeList == null)
         {
            continue;
         }

         for (int nAttr = 0, nAttrCount = attributeList.size(); nAttr < nAttrCount; ++nAttr)
         {
            Attribute baseAttr = (Attribute)attributeList.get(nAttr);
            Attribute derivedAttr = derivedClass.findAttribute(baseAttr.getName());

            if (derivedAttr == null)
            {
               derivedAttr = (Attribute)baseAttr.clone();
               derivedClass.addAttribute(derivedAttr);
            }
            else
            {
               if (!derivedAttr.isCompatibleWith(baseAttr))
               {
                  MetadataValidationException e = new MetadataValidationException(
                     "err.meta.incompatibleOverriddenAttribute",
                     new Object[]{derivedAttr.getName(), derivedClass.getName(), getName()});

                  derivedAttr.setProperties(e);
                  eh = addException(eh, e);
               }

               derivedAttr.deriveFrom(baseAttr);
            }

            completeAttributeDerivation(derivedAttr, baseAttr);
         }
      }

      // Derive the events

      for (int nEvent = 0, nEventCount = getEventCount(); nEvent < nEventCount; ++nEvent)
      {
         Event baseEvent = getEvent(nEvent);
         Selector derivedSelector = derivedClass.addSelector(baseEvent.getName());
         Member derivedMember = derivedSelector.findMember(baseEvent.getArgumentCount());
         Event derivedEvent = null;

         if (derivedMember == null)
         {
            derivedEvent = (Event)baseEvent.clone();
            derivedClass.addEvent(derivedEvent);
         }
         else if (derivedMember instanceof Event)
         {
            derivedEvent = (Event)derivedMember;

            if (!derivedEvent.isCompatibleWith(baseEvent))
            {
               derivedEvent = null;
            }
            else
            {
               // Resolve the actions: 1st pass
               // (The 2nd pass - resolveActions() - is after the action names fixup)

               int nCopiedActionCount = 0;
               for (int nAction = 0; nAction < baseEvent.getActionCount(); ++nAction)
               {
                  Action baseAction = baseEvent.getAction(nAction);
                  Action derivedAction = derivedEvent.findAction(baseAction.getName());

                  if (derivedAction == null)
                  {
                     derivedEvent.addAction((Action)baseAction.clone());
                     ++nCopiedActionCount;
                  }
                  else if (derivedAction.getType() != baseAction.getType())
                  {
                     MetadataValidationException e = new MetadataValidationException(
                        "err.meta.incompatibleOverriddenAction",
                        new Object[]{derivedAction.getName(), derivedEvent.getName(),
                           derivedClass.getName(), getName()});

                     derivedAction.setProperties(e);
                     eh = addException(eh, e);
                  }
               }

               derivedEvent.rotateActions(nCopiedActionCount);
               derivedEvent.inheritVariables(baseEvent);
               derivedEvent.inheritEventTypes(baseEvent);
            }
         }

         if (derivedEvent == null)
         {
            MetadataValidationException e = new MetadataValidationException(
               "err.meta.incompatibleOverriddenEvent",
               new Object[]{derivedMember.getName(), derivedClass.getName(), getName()});

            derivedMember.setProperties(e);
            eh = addException(eh, e);
         }
         else
         {
            completeEventDerivation(derivedEvent, baseEvent);
         }
      }

      return eh;
   }

   /**
    * Template method to complete an attribute derivation.
    * @param derived The derived attribute.
    * @param base The base attribute.
    */
   protected void completeAttributeDerivation(Attribute derived, Attribute base)
   {
      derived.setOrdinal(base.getOrdinal());
   }

   /**
    * Template method to complete an event derivation.
    * @param derived The derived event.
    * @param base The base event.
    */
   protected void completeEventDerivation(Event derived, Event base)
   {
   }

   /**
    * Resolves the attributes.
    * @param machine The VM for macro expansion.
    * @throws MetadataException if an error occurs.
    */
   public void resolveAttributes(final Machine machine)
   {
      ExceptionHolder eh = visit(new AttributeDependencyVisitor()
      {
         protected void computeDependency(Attribute attribute)
         {
            attribute.resolve(machine);
         }
      }, null);

      if (eh != null)
      {
         throw (UncheckedException)eh;
      }
   }

   /**
    * Computes the attribute inverse dependency.
    * @param depSet The attribute dependency set.
    * @throws MetadataException if an error occurs.
    * @see Attribute#computeInverseDependency(Holder)
    */
   public void computeInverseDependency(final Set depSet)
   {
      ExceptionHolder eh = visit(new AttributeDependencyVisitor()
      {
         protected void computeDependency(Attribute attribute)
         {
            attribute.computeInverseDependency(depSet);
         }
      }, null);

      if (eh != null)
      {
         throw (UncheckedException)eh;
      }
   }

   /**
    * Visits recursively all the attributes, including the derived classes.
    * @param visitor The attribute visitor.
    * @param eh The exception holder where to add the exceptions. Can be null.
    * @return The exception holder.
    */
   protected ExceptionHolder visit(AttributeVisitor visitor, ExceptionHolder eh)
   {
      for (int nStatic = 0; nStatic <= 1; ++nStatic)
      {
         List attrList = (nStatic != 0) ? m_staticAttributeList : m_instanceAttributeList;

         if (attrList == null)
         {
            continue;
         }

         for (int nAttr = 0, nAttrCount = attrList.size(); nAttr < nAttrCount; ++nAttr)
         {
            Attribute attr = (Attribute)attrList.get(nAttr);

            try
            {
               visitor.visit(attr);
            }
            catch (MetadataValidationException e)
            {
               eh = addException(eh, e);
            }
            catch (MetadataException e)
            {
               MetadataValidationException x = new MetadataValidationException(e);

               attr.setProperties(x);
               eh = addException(eh, e);
            }
         }
      }

      for (int nDerived = 0, nDerivedCount = getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         eh = getDerived(nDerived).visit(visitor, eh);
      }

      return eh;
   }

   /**
    * Adds an exception to an exception holder. Optionally creates the exception holder.
    * @param eh The exception holder or null to create a new one.
    * @param e The exception to add.
    * @return The exception holder.
    */
   protected final static ExceptionHolder addException(ExceptionHolder eh, Throwable e)
   {
      if (eh == null)
      {
         eh = new MetadataCompoundValidationException();
      }

      eh.addException(e);

      return eh;
   }

   /**
    * Additional first pass of inheritance resolution.
    * Creates additional persistence mappings.
    */
   public final void resolveInheritance1()
   {
      boolean bBaseCompatible = false;

      if (m_base != null)
      {
         // Inherit the persistence mapping

         if (m_persistenceMapping == null && m_base.m_persistenceMapping != null)
         {
            m_persistenceMapping = m_base.m_persistenceMapping.create();
            m_persistenceMapping.setMetaclass(this);
            m_persistenceMapping.setDataSource(m_base.m_persistenceMapping.getDataSource());
            bBaseCompatible = true;
         }
         else
         {
            bBaseCompatible = m_persistenceMapping != null &&
               m_base.m_persistenceMapping != null &&
               m_persistenceMapping.isCompatible(m_base.m_persistenceMapping);
         }
      }

      for (int nDerived = 0, nDerivedCount = getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         getDerived(nDerived).resolveInheritance1();
      }

      // Compute the cumulative dependency of the attributes
      if (bBaseCompatible)
      {
         for (int nStatic = 0; nStatic <= 1; ++nStatic)
         {
            List attrList = (nStatic != 0) ? m_base.m_staticAttributeList : m_base.m_instanceAttributeList;

            if (attrList == null)
            {
               continue;
            }

            for (int nAttr = 0, nAttrCount = attrList.size(); nAttr < nAttrCount; ++nAttr)
            {
               Attribute baseAttr = (Attribute)attrList.get(nAttr);
               Attribute derivedAttr = getDerivedAttribute(baseAttr);

               baseAttr.addCumulativeDependency(derivedAttr.getCumulativeDependency());
               baseAttr.addValueDeclarators(derivedAttr);
            }
         }
      }
   }

   /**
    * Aspect member inheritance resolution.
    * @param eh The exception holder where to add the exceptions. Can be null.
    * @return The exception holder.
    */
   protected ExceptionHolder resolveAspectMembers(ExceptionHolder eh)
   {
      if (m_nDirectAspectCount != 0)
      {
         for (Iterator attrItr = getAttributeIterator(); attrItr.hasNext();)
         {
            Attribute attribute = (Attribute)attrItr.next();

            if (attribute.getReadPrivilege() == null ||
               attribute.getUpdatePrivilege() == null ||
               attribute.getAccessAttribute() == null)
            {
               for (int nAspect = 0; nAspect < m_nDirectAspectCount; ++nAspect)
               {
                  ClassAspect aspect = (ClassAspect)getAspect(nAspect);
                  Attribute aspectAttribute = aspect.findAttribute(attribute.getName());

                  if (aspectAttribute != null)
                  {
                     try
                     {
                        if (attribute.getReadPrivilege() == null)
                        {
                           attribute.setReadPrivilege(aspectAttribute.getReadPrivilege());
                        }

                        if (attribute.getUpdatePrivilege() == null)
                        {
                           attribute.setUpdatePrivilege(aspectAttribute.getUpdatePrivilege());
                        }

                        if (attribute.getReverse() == null &&
                           aspectAttribute.getReverse() != null)
                        {
                           attribute.setReverse(aspectAttribute.getReverse());
                        }

                        if (attribute.getAccessAttribute() == null &&
                           aspectAttribute.getAccessAttribute() != null)
                        {
                           attribute.setAccessAttribute(getAttribute(aspectAttribute.getName()));
                        }
                     }
                     catch (MetadataValidationException e)
                     {
                        eh = addException(eh, e);
                     }
                     catch (UncheckedException e)
                     {
                        MetadataValidationException x = new MetadataValidationException(e);

                        attribute.setProperties(x);
                        eh = addException(eh, x);
                     }
                  }
               }
            }
         }

         for (int nEvent = 0, nEventCount = getEventCount(); nEvent < nEventCount; ++nEvent)
         {
            Event event = getEvent(nEvent);

            if (event.getPrivilege() == null ||
               event.getAccessAttribute() == null ||
               event.getTransactionMode() == Event.TX_DEFAULT ||
               event.getAudited() == null)
            {
               for (int nAspect = 0; nAspect < m_nDirectAspectCount; ++nAspect)
               {
                  ClassAspect aspect = (ClassAspect)getAspect(nAspect);
                  Event aspectEvent = aspect.findEvent(event.getName(), event.getArgumentCount());

                  if (aspectEvent != null)
                  {
                     try
                     {
                        if (event.getPrivilege() == null)
                        {
                           event.setPrivilege(aspectEvent.getPrivilege());
                        }

                        if (event.getAccessAttribute() == null &&
                           aspectEvent.getAccessAttribute() != null)
                        {
                           event.setAccessAttribute(getAttribute(aspectEvent.getAccessAttribute().getName()));
                        }

                        if (event.getTransactionMode() == Event.TX_DEFAULT)
                        {
                           event.setTransactionMode(aspectEvent.getTransactionMode());
                        }

                        if (event.getAudited() == null)
                        {
                           event.setAudited(aspectEvent.getAudited());
                        }
                     }
                     catch (MetadataValidationException e)
                     {
                        eh = addException(eh, e);
                     }
                     catch (UncheckedException e)
                     {
                        MetadataValidationException x = new MetadataValidationException(e);

                        event.setProperties(x);
                        eh = addException(eh, x);
                     }
                  }
               }
            }
         }
      }

      return eh;
   }

   /**
    * Second pass of inheritance resolution.
    * Resolves the persistence mappings, inherited privileges and access attributes.
    * @param urlMap The URL map to use for the validation function. May be null.
    * @param eh The exception holder where to add the exceptions. Can be null.
    * @return The exception holder.
    */
   private final ExceptionHolder resolveInheritance2(Lookup urlMap, ExceptionHolder eh)
   {
      eh = resolveAspectMembers(eh);

      boolean bWhereDiff = true;
      boolean bValidationDiff = true;

      if (m_base != null)
      {
         // Resolve the where clause
         if (m_where == null)
         {
            bWhereDiff = false;
         }
         else
         {
            bWhereDiff = !ObjUtil.equal(m_where, m_base.m_where);
         }

         if (!bWhereDiff)
         {
            m_where = m_base.m_where;
         }

         if (m_validation == Undefined.VALUE)
         {
            bValidationDiff = false;
         }
         else
         {
            bValidationDiff = !ObjUtil.equal(m_validation, m_base.m_validation);
         }

         if (!bValidationDiff)
         {
            m_validation = m_base.m_validation;
         }
      }

      for (int nAspect = 0, nAspectCount = getAspectCount(); nAspect < nAspectCount; ++nAspect)
      {
         ClassAspect aspect = (ClassAspect)getAspect(nAspect);

         if (aspect.getWhere() != null)
         {
            if (bWhereDiff || !m_base.hasAspect(aspect))
            {
               m_where = Pair.commutative(Symbol.AND, m_where, aspect.getWhere());
            }
         }

         if (aspect.getValidation() != Undefined.VALUE)
         {
            if (bValidationDiff || !m_base.hasAspect(aspect))
            {
               Lookup aspectTextPosMap = aspect.getTextPositionMap();

               if (m_textPosMap != null && aspectTextPosMap != null)
               {
                  String sAspectURL = "class:" + aspect.getName() + "$validation";

                  for (Lookup.Iterator it = aspectTextPosMap.iterator(); it.hasNext();)
                  {
                     it.next();

                     m_textPosMap.put(it.getKey(), it.getValue());
                     urlMap.put(it.getValue(), sAspectURL);
                  }
               }

               if (m_validation == Undefined.VALUE)
               {
                  m_validation = aspect.getValidation();
               }
               else
               {
                  m_validation = Pair.list(Pair.list(Symbol.LAMBDA, Pair.list(VALIDATION_ARG),
                     Pair.list(Symbol.IF, Pair.list(Symbol.EQ_P, VALIDATION_ARG, Boolean.TRUE),
                        m_validation, VALIDATION_ARG)), aspect.getValidation());
               }
            }
         }
      }

      if (m_base != null)
      {
         // Resolve the attributes

         for (int nStatic = 0; nStatic <= 1; ++nStatic)
         {
            boolean bStatic = (nStatic != 0);
            List baseList = (bStatic) ? m_base.m_staticAttributeList : m_base.m_instanceAttributeList;

            if (baseList == null)
            {
               continue;
            }

            List derivedList = (bStatic) ? m_staticAttributeList : m_instanceAttributeList;

            for (int nAttr = 0, nAttrCount = baseList.size(); nAttr < nAttrCount; ++nAttr)
            {
               Attribute baseAttr = (Attribute)baseList.get(nAttr);
               Attribute derivedAttr = (Attribute)derivedList.get(nAttr);

               try
               {
                  if (derivedAttr.getReadPrivilege() == null)
                  {
                     derivedAttr.setReadPrivilege(baseAttr.getReadPrivilege());
                  }

                  if (derivedAttr.getUpdatePrivilege() == null)
                  {
                     derivedAttr.setUpdatePrivilege(baseAttr.getUpdatePrivilege());
                  }

                  derivedAttr.setAccessAttribute(getDerivedAttribute((derivedAttr.getAccessAttribute() != null) ?
                     derivedAttr.getAccessAttribute() : baseAttr.getAccessAttribute()));

                  if (derivedAttr.getVisibility() > baseAttr.getVisibility())
                  {
                     throw new MetadataException("err.meta.reducedAttributeVisibility",
                        new Object[]{derivedAttr.getName(), getName(), m_base.getName()});
                  }

                  if (derivedAttr.getCascadeMode() == Attribute.CASCADE_DEFAULT)
                  {
                     derivedAttr.setCascadeMode(baseAttr.getCascadeMode());
                  }

                  if (derivedAttr.getDeclarator() != this)
                  {
                     derivedAttr.setCached(baseAttr.isCached());
                  }
               }
               catch (MetadataValidationException e)
               {
                  eh = addException(eh, e);
               }
               catch (UncheckedException e)
               {
                  MetadataValidationException x = new MetadataValidationException(e);

                  derivedAttr.setProperties(x);
                  eh = addException(eh, x);
               }
            }
         }

         // Resolve the events

         for (int nEvent = 0, nEventCount = m_base.getEventCount(); nEvent < nEventCount; ++nEvent)
         {
            Event baseEvent = m_base.getEvent(nEvent);
            Event derivedEvent = (Event)getSelector(baseEvent.getName()).getMember(baseEvent.getArgumentCount());

            try
            {
               if (derivedEvent.getPrivilege() == null)
               {
                  derivedEvent.setPrivilege(baseEvent.getPrivilege());
               }

               derivedEvent.setAccessAttribute(getDerivedAttribute((derivedEvent.getAccessAttribute() != null) ?
                  derivedEvent.getAccessAttribute() : baseEvent.getAccessAttribute()));

               if (derivedEvent.getTransactionMode() == Event.TX_DEFAULT)
               {
                  derivedEvent.setTransactionMode(baseEvent.getTransactionMode());
               }

               if (derivedEvent.getAudited() == null)
               {
                  derivedEvent.setAudited(baseEvent.getAudited());
               }

               if (derivedEvent.getVisibility() > baseEvent.getVisibility())
               {
                  throw new MetadataException("err.meta.reducedEventVisibility",
                     new Object[]{derivedEvent.getName(), getName(), m_base.getName()});
               }
            }
            catch (MetadataValidationException e)
            {
               eh = addException(eh, e);
            }
            catch (UncheckedException e)
            {
               MetadataValidationException x = new MetadataValidationException(e);

               derivedEvent.setProperties(x);
               eh = addException(eh, x);
            }
         }

         // Check the visibility
         if (m_nVisibility > m_base.m_nVisibility)
         {
            MetadataValidationException e = new MetadataValidationException(
               "err.meta.reducedClassVisibility", new Object[]{getName(), m_base.getName()});

            setProperties(e);
            eh = addException(eh, e);
         }

         // Resolve the name attribute
         if (m_nameAttribute == null)
         {
            m_nameAttribute = getDerivedAttribute(m_base.getNameAttribute());
         }
      }

      for (int nStatic = 0; nStatic <= 1; ++nStatic)
      {
         List attrList = (nStatic != 0) ? m_staticAttributeList : m_instanceAttributeList;

         if (attrList == null)
         {
            continue;
         }

         for (int nAttr = 0, nAttrCount = attrList.size(); nAttr < nAttrCount; ++nAttr)
         {
            Attribute attr = (Attribute)attrList.get(nAttr);

            // Set the default cascade mode to CASCADE_NONE.
            if (attr.getCascadeMode() == Attribute.CASCADE_DEFAULT)
            {
               attr.setCascadeMode(Attribute.CASCADE_NONE);
            }
         }
      }

      for (int nEvent = 0, nEventCount = getEventCount(); nEvent < nEventCount; ++nEvent)
      {
         Event event = getEvent(nEvent);

         // Set the default transaction mode to TX_SUPPORTED.
         if (event.getTransactionMode() == Event.TX_DEFAULT)
         {
            event.setTransactionMode(Event.TX_SUPPORTED);
         }
      }

      // Setup the class-level security and transaction mode

      Event event = findEvent("create", 0);

      if (event != null)
      {
         setCreatePrivilege(event.getPrivilege());
         setCreateTransactionMode(event.getTransactionMode());
      }

      event = findEvent("read", 6);

      if (event != null)
      {
         setReadPrivilege(event.getPrivilege());
         setReadAccessAttribute(event.getAccessAttribute());
      }

      event = findEvent("update", 0);

      if (event != null)
      {
         setUpdatePrivilege(event.getPrivilege());
         setUpdateAccessAttribute(event.getAccessAttribute());
         setUpdateTransactionMode(event.getTransactionMode());
      }

      event = findEvent("delete", 0);

      if (event != null)
      {
         setDeletePrivilege(event.getPrivilege());
         setDeleteAccessAttribute(event.getAccessAttribute());
      }

      if (m_persistenceMapping != null)
      {
         try
         {
            m_persistenceMapping.resolveInheritance();

            if (m_base != null)
            {
               PersistenceMapping baseMapping = m_base.getPersistenceMapping();

               if (baseMapping != null &&
                  baseMapping.isCompatible(m_persistenceMapping))
               {
                  Attribute typeCodeAttr = m_persistenceMapping.getTypeCodeAttribute();

                  if (typeCodeAttr != null &&
                     getDerivedAttribute(baseMapping.getTypeCodeAttribute()) == typeCodeAttr)
                  {
                     m_persistenceRoot = m_base.m_persistenceRoot;
                  }
                  else
                  {
                     for (Metaclass base = m_base; base != null; base = base.getBase())
                     {
                        base = base.getPersistenceRoot();

                        if (base.getPersistenceMapping() != null &&
                           (typeCodeAttr == null || base.getDerivedAttribute(typeCodeAttr) != typeCodeAttr))
                        {
                           base.addPersistenceAlias(this);
                        }
                     }
                  }
               }

               for (int nAttr = 0, nAttrCount = m_instanceAttributeList.size(); nAttr < nAttrCount; ++nAttr)
               {
                  Attribute attr = (Attribute)m_instanceAttributeList.get(nAttr);

                  if (attr.isPersistent() && attr.getRootDeclarator() != this)
                  {
                     Attribute baseAttr = m_base.getInstanceAttribute(attr.getOrdinal());

                     if (baseAttr.isPersistent() &&
                        attr.getPersistenceMapping().isAliasOf(baseAttr.getPersistenceMapping()) &&
                        ObjUtil.equal(attr.getWhere(), baseAttr.getWhere()))
                     {
                        attr.setPersistenceRoot(baseAttr.getPersistenceRoot());
                     }
                  }
               }
            }
         }
         catch (MetadataValidationException e)
         {
            eh = addException(eh, e);
         }
         catch (UncheckedException e)
         {
            MetadataValidationException x = new MetadataValidationException(e);

            setProperties(x);
            eh = addException(eh, x);
         }
      }

      return eh;
   }

   /**
    * Third pass of inheritance resolution.
    * Assigns default privileges and access attributes
    * to the attributes, using the class ones.
    */
   private final void resolveInheritance3()
   {
      for (int nStatic = 0; nStatic <= 1; ++nStatic)
      {
         List attributeList = (nStatic != 0) ? m_staticAttributeList : m_instanceAttributeList;

         if (attributeList == null)
         {
            continue;
         }

         for (int nAttr = 0, nAttrCount = attributeList.size(); nAttr < nAttrCount; ++nAttr)
         {
            Attribute attribute = (Attribute)attributeList.get(nAttr);

            if (attribute.getReadPrivilege() == null)
            {
               attribute.setReadPrivilege(m_readPrivilege);
            }

            if (!attribute.isCollection())
            {
               if (attribute.getUpdatePrivilege() == null)
               {
                  attribute.setUpdatePrivilege(m_updatePrivilege);
               }

               if (!attribute.isStatic() && attribute.getAccessAttribute() == null)
               {
                  attribute.setAccessAttribute(m_updateAccessAttribute);
               }
            }
         }
      }
   }

   /**
    * Fourth pass of inheritance resolution.
    * Runs an additional pass on the persistence mappings.
    */
   public final void resolveInheritance4()
   {
      if (m_persistenceMapping != null)
      {
         m_persistenceMapping.resolveInheritance2();

         for (int i = 0, n = getInstanceAttributeCount(); i != n; ++i)
         {
            Attribute attribute = getInstanceAttribute(i);
            AttributeMapping mapping = attribute.getPersistenceMapping();

            if (mapping != null)
            {
               attribute.setMaxLength();

               for (int k = i - 1; k >= 0; --k)
               {
                  Attribute attribute2 = getInstanceAttribute(k);
                  AttributeMapping mapping2 = attribute2.getPersistenceMapping();

                  if (mapping2 != null)
                  {
                     if (mapping.isAliasOf(mapping2))
                     {
                        // Insert into a circular list
                        attribute.setAlias((attribute2.getAlias() == null) ? attribute2 : attribute2.getAlias());
                        attribute2.setAlias(attribute);

                        break;
                     }
                  }
               }
            }
         }
      }

      for (int nDerived = 0, nDerivedCount = getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         getDerived(nDerived).resolveInheritance4();
      }
   }

   /**
    * Finds an event with a given name and number of arguments.
    * @param sName The event name.
    * @param nArgCount The event argument count.
    * @return The event metadata object or null if not found.
    */
   public final Event findEvent(String sName, int nArgCount)
   {
      Selector selector = findSelector(sName);

      if (selector != null)
      {
         Member member = selector.findMember(nArgCount);

         if (member instanceof Event)
         {
            return (Event)member;
         }
      }

      return null;
   }

   /**
    * Sorts the actions within the events, computes the action invocation
    * table and compiles the events.
    * @param machine The VM to use for compilation.
    */
   public final void compile(Machine machine)
   {
      Metaclass sysSyncClass = ((Metadata)machine.getContext().getContextMetadata())
         .findMetaclass(Metadata.SYNC_CLASS_CLASS_NAME);
      ExceptionHolder eh = (sysSyncClass == null) ? null : generateSyncDependencyActions(sysSyncClass, null);

      eh = compile(machine, eh);

      if (eh != null)
      {
         throw (UncheckedException)eh;
      }
   }

   /**
    * Generates actions necessary to add change information that affects synchronization to unit of work,
    * in order for synchronization engine to react properly to these changes.
    * @param syncClassMetaclass Metaclass derived from SysSyncClass, that contains information required to generate the
    * action.
    * @param eh The exception holder where to add the exceptions or null to create a new one.
    * @return The exception holder.
    */
   private final ExceptionHolder generateSyncDependencyActions(Metaclass syncClassMetaclass, ExceptionHolder eh)
   {
      Attribute dependencyListAttr = syncClassMetaclass.findAttribute("DEPENDENCY_LIST");

      Attribute attr = (dependencyListAttr != null)
         ? dependencyListAttr : syncClassMetaclass.findAttribute("syncDependency");

      if (attr == null)
      {
         return eh;
      }

      Pair value = (Pair)attr.getValue();
      boolean bValid = (value.getHead() == Symbol.QUOTE) && (value.getTail() instanceof Pair);

      if (bValid)
      {
         value = (Pair)((Pair)value.getTail()).getHead();

         while (bValid && value != null)
         {
            bValid = (value.getHead() instanceof Pair);

            if (bValid)
            {
               Pair classAttributes = (Pair)value.getHead();

               bValid = (classAttributes.getHead() instanceof Symbol)
                  && (((Pair)classAttributes.getTail()).getHead() instanceof Pair);

               if (bValid)
               {
                  String className = ((Symbol)classAttributes.getHead()).getName();
                  Pair attributes = (Pair)((Pair)classAttributes.getTail()).getHead();
                  Metaclass metaclass = syncClassMetaclass.getMetadata().findMetaclass(className);

                  bValid = (metaclass != null);

                  if (bValid)
                  {
                     generateSyncDependencyAction(metaclass, attributes);
                  }
               }
            }

            value = (Pair)value.getTail();
         }
      }

      if (!bValid)
      {
         MetadataValidationException x = new MetadataValidationException("err.meta.syncDepClassSyntax",
            new Object[] {attr.getName(), syncClassMetaclass.getName()});

         attr.setProperties(x);
         x.setProperty("item", "value");
         eh = addException(eh, x);

         return eh;
      }

      for (int nDerived = 0, nDerivedCount = syncClassMetaclass.getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         eh = generateSyncDependencyActions(syncClassMetaclass.getDerived(nDerived), eh);
      }

      return eh;
   }

   /**
    * Generates actions necessary to add change information that affects synchronization to unit of work,
    * in order for synchronization engine to react properly to these changes.
    * @param dependentMetaclass A metaclass that affects synchronization.
    * @param attributes A list of attributes of this metaclass, affecting synchronization.
    */
   private void generateSyncDependencyAction(Metaclass dependentMetaclass, Pair attributes)
   {
      dependentMetaclass.findEvent("commit", 0).generateSyncDependencyAction(attributes);
      dependentMetaclass.findEvent("delete", 0).generateSyncDependencyAction(attributes);

      for (Iterator itr = dependentMetaclass.getDerivedIterator(); itr.hasNext();)
      {
         generateSyncDependencyAction((Metaclass)itr.next(), attributes);
      }
   }

   /**
    * Sorts the actions within the events, computes the action invocation
    * table and compiles the events.
    * @param machine The VM to use for compilation.
    * @param eh The exception holder where to add the exceptions or null to create a new one.
    * @return The exception holder.
    */
   private final ExceptionHolder compile(Machine machine, ExceptionHolder eh)
   {
      verifyNotReadOnly();

      Lookup urlMap = (m_textPosMap == null) ? null : new IdentityHashTab(m_textPosMap.size() * 2);

      eh = resolveInheritance2(urlMap, eh);
      m_logger = Logger.getLogger(LOG_CATEGORY_PREFIX + getName());
      setCurrent();

      // Compile the validation function
      if (m_validation != Undefined.VALUE)
      {
         if (m_base != null && m_validation == m_base.m_validation)
         {
            m_validationFunction = m_base.m_validationFunction;
         }
         else
         {
            try
            {
               Object expr = new Pair(Symbol.LAMBDA, new Pair(new Pair(Symbol.THIS), new Pair(m_validation)));

               if (m_textPosMap != null)
               {
                  TextPosition pos = new TextPosition(0,0);

                  m_textPosMap.put(expr, pos);
                  urlMap.put(pos, "class:" + getName() + "$validation");
               }

               m_validationFunction = new Compiler().compile(expr, m_textPosMap, urlMap, machine, false);
            }
            catch (Exception e)
            {
               MetadataValidationException x;

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

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

               eh = addException(eh, x);
            }
         }
      }

      // Compile the attributes
      for (int nStatic = 0; nStatic <= 1; ++nStatic)
      {
         List attrList = (nStatic != 0) ? m_staticAttributeList : m_instanceAttributeList;

         if (attrList == null)
         {
            continue;
         }

         for (int nAttr = 0, nAttrCount = attrList.size(); nAttr < nAttrCount; ++nAttr)
         {
            Attribute attr = (Attribute)attrList.get(nAttr);

            if (attr.getDeclarator() == this)
            {
               try
               {
                  attr.compile(machine);
               }
               catch (ValidationException e)
               {
                  eh = addException(eh, e);
               }
               catch (UncheckedException e)
               {
                  MetadataValidationException x = new MetadataValidationException(e);

                  attr.setProperties(x);
                  eh = addException(eh, x);
               }
            }
            else
            {
               attr.setFunctions((Attribute)((nStatic != 0) ? m_base.m_staticAttributeList :
                  m_base.m_instanceAttributeList).get(attr.getOrdinal()));
               attr.setTextPositionMap(null);
            }
         }
      }

      // Compile the events
      for (int nEvent = 0; nEvent < getEventCount(); ++nEvent)
      {
         Event baseEvent = getEvent(nEvent);

         try
         {
            if (baseEvent.getRootDeclarator() == this)
            {
               baseEvent.generateCreateFlowAction();
               baseEvent.generateTriggerFlowAction();
               baseEvent.resolveActions();
            }

            baseEvent.sortActions();

            for (int nDerived = 0, nDerivedCount = getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
            {
               Metaclass derivedClass = getDerived(nDerived);
               Event derivedEvent = (Event)derivedClass.getSelector(baseEvent.getName()).findMember(baseEvent.getArgumentCount());

               derivedEvent.generateCreateFlowAction();
               derivedEvent.generateTriggerFlowAction();
               derivedEvent.resolveActions();
            }
         }
         catch (ValidationException e)
         {
            eh = addException(eh, e);
         }
         catch (UncheckedException e)
         {
            MetadataValidationException x = new MetadataValidationException(e);

            baseEvent.setProperties(x);
            eh = addException(eh, x);
         }

         // Compile only after the derived event action processing
         // so that action code could be copied
         baseEvent.compile(machine);
      }

      clearCurrent();

      for (int nDerived = 0, nDerivedCount = getDerivedCount(); nDerived < nDerivedCount; ++nDerived)
      {
         eh = getDerived(nDerived).compile(machine, eh);
      }

      setTextPositionMap(null);
      resolveInheritance3();

      return eh;
   }

   /**
    * Sets the current class in the global environment.
    */
   protected void setCurrent()
   {
      m_metadata.getGlobalEnvironment().defineVariable(Symbol.SYS_CURRENT_LOGGER, m_logger);
      m_metadata.getGlobalEnvironment().defineVariable(SYS_CURRENT_CLASS, this);
   }

   /**
    * Removes the current attribute from the global environment.
    */
   protected void clearCurrent()
   {
      m_metadata.getGlobalEnvironment().removeVariable(SYS_CURRENT_CLASS);
      m_metadata.getGlobalEnvironment().removeVariable(Symbol.SYS_CURRENT_LOGGER);
   }

   /**
    * Assigns ordinal numbers where necessary to all the attributes
    * and sorts the instance and static attributes according to
    * their ordinal number.
    * @param list The attribute list to sort.
    */
   private final static void sortAttributes(List list)
   {
      int nOrdinal = -1;

      for (int i = 0, nCount = list.size(); i < nCount; ++i)
      {
         nOrdinal = Math.max(nOrdinal, ((Attribute)list.get(i)).getOrdinal());
      }

      for (int i = 0, nCount = list.size(); i < nCount; ++i)
      {
         Attribute attr = (Attribute)list.get(i);

         if (attr.getOrdinal() < 0)
         {
            attr.setOrdinal(++nOrdinal);
         }
      }

      Collections.sort(list, ATTRIBUTE_ORDINAL_COMPARATOR);
   }

   /**
    * @param where The where clause. Can be null.
    * @see PersistenceMapping#getSortKeys(Attribute[], PersistenceMapping[], Attribute[])
    */
   public final Pair getSortKeys(Attribute[] assocs, Object where)
   {
      if (m_persistenceMapping == null)
      {
         return null;
      }

      PersistenceMapping[] mappings = null;
      Set restrictionSet = new HashHolder(1);

      addRestrictions(restrictionSet, getWhere());
      addRestrictions(restrictionSet, where);

      if (assocs != null)
      {
         mappings = new PersistenceMapping[assocs.length];

         for (int i = 0; i < assocs.length; ++i)
         {
            Attribute attribute = assocs[i];

            mappings[i] = attribute.getMetaclass().getPersistenceMapping();
            addRestrictions(restrictionSet, attribute.getWhere());
         }
      }

      return m_persistenceMapping.getSortKeys(assocs, mappings, (restrictionSet.isEmpty()) ? null :
            (Attribute[])restrictionSet.toArray(new Attribute[restrictionSet.size()]));
   }

   /**
    * Adds equality restriction attributes from an S-expression to a set.
    * @param attribSet The restriction attribute set.
    * @param expr The S-expression to analyze.
    */
   protected final void addRestrictions(Set attribSet, Object expr)
   {
      Attribute attribute = getAttribute(expr);

      if (attribute != null)
      {
         if (attribute.getType() == Primitive.BOOLEAN)
         {
            if (attribute.isPersistent())
            {
               attribSet.add(attribute);
            }
            else if (attribute.isCalculated())
            {
               addRestrictions(attribSet, attribute.getValue());
            }
         }
      }
      else if (expr instanceof Pair)
      {
         Pair pair = (Pair)expr;
         Object head = pair.getHead();

         if (head == Symbol.AND)
         {
            while (pair.getTail() instanceof Pair)
            {
               pair = pair.getNext();
               addRestrictions(attribSet, pair.getHead());
            }
         }
         else if (head == Symbol.EQ)
         {
            while (pair.getTail() instanceof Pair)
            {
               pair = pair.getNext();
               attribute = getAttribute(pair.getHead());

               if (attribute != null && attribute.isPersistent())
               {
                  attribSet.add(attribute);
               }
            }
         }
         else if (head == Symbol.IN_P)
         {
            if (pair.getTail() instanceof Pair)
            {
               attribute = getAttribute(pair.getHead());

               if (attribute != null && attribute.isPersistent())
               {
                  attribSet.add(attribute);
               }
            }
         }
         else if (head == Symbol.IF)
         {
            if (pair.getTail() instanceof Pair)
            {
               pair = pair.getNext();

               if (pair.getTail() instanceof Pair)
               {
                  for (int i = 0; i < GENERALLY_TRUE_EXPRESSIONS.length; ++i)
                  {
                     if (GENERALLY_TRUE_EXPRESSIONS[i].equals(pair.getHead()))
                     {
                        addRestrictions(attribSet, pair.getNext().getHead());
                        break;
                     }
                  }
               }
            }
         }
         else if (head == Symbol.FOLD)
         {
            if (pair.getTail() instanceof Pair)
            {
               pair = pair.getNext();

               if (pair.getTail() instanceof Pair)
               {
                  addRestrictions(attribSet, pair.getNext().getHead());
               }
            }
         }
      }
   }

   /**
    * Gets an attribute from an S-expression.
    * @param expr The S-expression.
    * @return The attribute, or null if the expression does not designate an attribute.
    */
   protected final Attribute getAttribute(Object expr)
   {
      if (expr instanceof Pair)
      {
         Pair pair = (Pair)expr;

         if (pair.getHead() == Symbol.AT && pair.getTail() instanceof Pair)
         {
            pair = pair.getNext();

            if (pair.getHead() instanceof Symbol && pair.getTail() == null)
            {
               return findAttribute((Symbol)pair.getHead());
            }
         }
      }
      else if (expr instanceof Symbol)
      {
         return findAttribute((Symbol)expr);
      }

      return null;
   }

   /**
    * @return List of unique secondary keys.
    * @see PersistenceMapping#getUniqueKeys()
    */
   public final Pair getUniqueKeys()
   {
      if (m_persistenceMapping != null)
      {
         return m_persistenceMapping.getUniqueKeys();
      }

      return null;
   }

   /**
    * Adds a new attribute to a dependency list.
    * @param pair The dependency list, to which to add the attribute. The first pair is not used.
    * @param attribute The attribute to add. Can be null.
    * @param bStatic True to look for static dependencies only.
    * @return The pair, in which head the attribute was added, or null for an invalid attribute.
    */
   private final static Pair addDependency(Pair pair, Attribute attribute, boolean bStatic)
   {
      if (attribute == null || attribute.isStatic() != bStatic)
      {
         return null;
      }

      while (pair.getTail() != null)
      {
         pair = pair.getNext();

         if (pair.getHead() == attribute ||
            pair.getHead() instanceof Pair && ((Pair)pair.getHead()).getHead() == attribute)
         {
            return pair;
         }
      }

      pair.setTail(new Pair(attribute));

      return pair.getNext();
   }

   /**
    * Adds dependency for expressions like (... (lambda ... ) expr1 ... exprN).
    * @param pair The pair containing the lambda arguments in its head (i.e. the next pair after lambda).
    * @param expr The list of expressions expr1 .. exprN.
    * @param bStatic True to look for static dependencies only.
    * @param dep The list to accumulate the dependencies in. The head is true if
    *           dependencies are known fully.
    * @param local The top local variable binding. Can be null.
    * @param machine The VM from where to get and expand the macros.
    * @return The computed dependency list. Can be null.
    */
   protected Pair addFrameDependency(Pair pair, Pair expr, boolean bStatic, Pair dep, Local local, Machine machine)
   {
      Pair adep = null;
      Object head = pair.getHead();

      if (head instanceof Symbol)
      {
         local = new Local((Symbol)head, null, local);
      }
      else if (head instanceof Pair)
      {
         boolean bFirst = true;

         for (Pair arg = (Pair)head;; bFirst = false)
         {
            head = arg.getHead();

            Pair edep = (expr == null) ? null : dependency(expr.getHead(), bStatic, dep, local, machine);

            adep = (bFirst) ? edep : null;

            if (head instanceof Symbol)
            {
               local = new Local((Symbol)head, adep, local);
            }

            if (expr != null)
            {
               if (expr.getTail() instanceof Pair)
               {
                  expr = expr.getNext();
               }
               else
               {
                  if (expr.getTail() != null)
                  {
                     adep = null;
                  }

                  expr = null;
               }
            }

            head = arg.getTail();

            if (head instanceof Pair)
            {
               arg = arg.getNext();
            }
            else
            {
               if (head instanceof Symbol)
               {
                  local = new Local((Symbol)head, null, local);
               }

               break;
            }
         }
      }

      while (expr != null)
      {
         adep = null;
         dependency(expr.getHead(), bStatic, dep, local, machine);

         if (expr.getTail() instanceof Pair)
         {
            expr = expr.getNext();
         }
         else
         {
            break;
         }
      }

      while (pair.getTail() instanceof Pair)
      {
         pair = pair.getNext();
         dependency(pair.getHead(), bStatic, dep, local, machine);
      }

      return adep;
   }

   /**
    * Finds an attribute name from an expression ((quote name)).
    * @param pair The expression.
    * @return The attribute name or null if not found.
    */
   protected static String findAttributeName(Pair pair)
   {
      if (pair != null && pair.getHead() instanceof Pair && pair.getTail() == null)
      {
         pair = (Pair)pair.getHead();

         if (pair.getHead() == Symbol.QUOTE &&
            pair.getTail() instanceof Pair)
         {
            pair = pair.getNext();

            if (pair.getHead() instanceof Symbol && pair.getTail() == null)
            {
               return pair.getHead().toString();
            }
         }
      }

      return null;
   }

   /**
    * Computes the dependency list of an S-expression relative to this class.
    * @param expr The S-expression.
    * @param bStatic True to look for static dependencies only.
    * @param dep The list to accumulate the dependencies in. The head is true if
    *           dependencies are known fully.
    * @param local The top local variable binding. Can be null.
    * @param machine The VM from where to get and expand the macros.
    * @return The computed dependency list. Can be null.
    */
   protected final Pair dependency(Object expr, boolean bStatic, final Pair dep, Local local, Machine machine)
   {
      if (expr instanceof Pair)
      {
         Pair adep = dep;
         Pair pair = (Pair)expr;
         Object obj = pair.getHead();
         Symbol sym = null;
         boolean bGlobal = false;

         // Extract the function name for simple function calls
         if (obj instanceof Symbol)
         {
            sym = (Symbol)pair.getHead();
         }
         else if (obj instanceof Pair)
         {
            Pair head = (Pair)obj;

            // (global <sym>)
            if (head.getHead() == Symbol.GLOBAL &&
               (obj = head.getTail()) instanceof Pair &&
               (head = (Pair)obj).getTail() == null &&
               (obj = head.getHead()) instanceof Symbol)
            {
               sym = (Symbol)obj;
               bGlobal = true;
            }
         }

         if (sym != null)
         {
            if (pair.getTail() == null || pair.getTail() instanceof Pair)
            {
               pair = pair.getNext();

               Local var = (bGlobal) ? null : Local.find(sym, local);

               if (var != null)
               {
                  adep = var.dep;
               }
               else
               {
                  obj = machine.getGlobalEnvironment().findVariable(sym);

                  if (obj instanceof Macro && sym != Symbol.AND && sym != Symbol.OR && sym != Symbol.ANY)
                  {
                     return dependency(machine.invoke((Function)obj, pair), bStatic, dep, local, machine);
                  }

                  if (sym == Symbol.THIS)
                  {
                     if (!bGlobal)
                     {
                        String sName = findAttributeName(pair);

                        if (sName != null)
                        {
                           pair = addDependency(dep, findAttribute(sName), bStatic);

                           if (pair == null)
                           {
                              dep.setHead(Boolean.FALSE);
                           }

                           return pair;
                        }

                        dep.setHead(Boolean.FALSE);
                     }
                  }
                  else if (sym == Symbol.QUOTE)
                  {
                     return null;
                  }
                  else if (sym == Symbol.DECLARE)
                  {
                     Pair head;

                     // Anything other than (declare scope client) disables full dependency
                     if (pair.getHead() == Symbol.SCOPE &&
                        (obj = pair.getTail()) instanceof Pair &&
                        (obj = (head = (Pair)obj).getHead()) instanceof Symbol &&
                        ((Symbol)obj).getName().equals("client") &&
                        head.getTail() == null)
                     {
                        dep.setHead(Boolean.FALSE);
                     }

                     return null;
                  }
                  else if (sym == Symbol.SET || sym == Symbol.DEFINE)
                  {
                     dep.setHead(Boolean.FALSE);

                     if (pair.getTail() == null || pair.getTail() instanceof Pair)
                     {
                        pair = pair.getNext();
                     }
                  }
                  else if (sym == Symbol.LAMBDA || sym == Symbol.MACRO)
                  {
                     dep.setHead(Boolean.FALSE);

                     return addFrameDependency(pair, null, bStatic, dep, local, machine);
                  }
                  else if (sym == Symbol.IF || sym == Symbol.BEGIN || sym == Symbol.OR || sym == Symbol.AND)
                  {
                     // Supported for client-side calculation
                  }
                  else if (sym == Symbol.FILTER || sym == Symbol.FOR_EACH || sym == Symbol.MAP)
                  {
                     if (pair.getHead() instanceof Pair && pair.getTail() instanceof Pair)
                     {
                        Pair head = (Pair)pair.getHead();

                        if (head.getHead() == Symbol.LAMBDA && head.getTail() instanceof Pair)
                        {
                           adep = addFrameDependency(head.getNext(), pair.getNext(), bStatic, adep, local, machine);

                           return (sym == Symbol.FILTER) ? adep : null;
                        }
                     }
                  }
                  else if (sym == Symbol.VALUE_COLLECTION)
                  {
                     if (pair.getTail() == null)
                     {
                        return dependency(pair.getHead(), bStatic, adep, local, machine);
                     }
                  }
                  else if (sym == Symbol.INSTANCE_P)
                  {
                     adep = dependency(pair.getHead(), bStatic, dep, local, machine);

                     if (adep != null && !bStatic)
                     {
                        boolean bPair = (adep.getHead() instanceof Pair);

                        if (bPair)
                        {
                           adep = (Pair)adep.getHead();
                        }

                        Attribute attribute = (Attribute)adep.getHead();
                        Type type = attribute.getType();

                        if (!type.isPrimitive())
                        {
                           if (!bPair)
                           {
                              adep.setHead(new Pair(adep.getHead()));
                              adep = (Pair)adep.getHead();
                           }

                           Metaclass metaclass = (Metaclass)type;
                           PersistenceMapping mapping = null;
                           Attribute typeCodeAttribute = null;

                           for (Metaclass base = metaclass; base != null; base = base.getBase())
                           {
                              PersistenceMapping baseMapping = base.getPersistenceMapping();

                              if (baseMapping != null)
                              {
                                 if (mapping != null && !mapping.isCompatible(baseMapping))
                                 {
                                    break;
                                 }

                                 mapping = baseMapping;
                                 typeCodeAttribute = mapping.getTypeCodeAttribute();

                                 if (typeCodeAttribute != null)
                                 {
                                    break;
                                 }
                              }
                           }

                           if (typeCodeAttribute != null)
                           {
                              addDependency(adep, metaclass.getDerivedAttribute(typeCodeAttribute), bStatic);
                           }
                        }
                     }

                     return null;
                  }
                  else if (dep.getHead() != Boolean.FALSE && !m_metadata.isClientSymbol(sym))
                  {
                     dep.setHead(Boolean.FALSE);
                  }
               }
            }
            else
            {
               dep.setHead(Boolean.FALSE);

               return null;
            }
         }
         else
         {
            Object head = pair.getHead();

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

               if (lambda.getHead() == Symbol.LAMBDA && lambda.getTail() instanceof Pair && pair.getTail() instanceof Pair)
               {
                  // Dynamic derived association expansion: ((lambda (this) ... filter ...) assoc)
                  return addFrameDependency(lambda.getNext(), pair.getNext(), bStatic, dep, local, machine);
               }
            }

            adep = dependency(head, bStatic, dep, local, machine);

            if (pair.getTail() instanceof Pair)
            {
               pair = pair.getNext();
            }
            else
            {
               pair = null;
            }
         }

         if (adep != dep)
         {
            if (adep != null && !bStatic)
            {
               String sName = findAttributeName(pair);

               if (sName != null)
               {
                  boolean bPair = (adep.getHead() instanceof Pair);

                  if (bPair)
                  {
                     adep = (Pair)adep.getHead();
                  }
                 
                  Attribute attribute = (Attribute)adep.getHead();
                  Type type = attribute.getType();

                  if (type.isPrimitive())
                  {
                     dep.setHead(Boolean.FALSE);

                     return null;
                  }

                  if (!bPair)
                  {
                     adep.setHead(new Pair(adep.getHead()));
                     adep = (Pair)adep.getHead();
                  }

                  adep = addDependency(adep, ((Metaclass)type).findAttribute(sName), bStatic);

                  if (adep == null)
                  {
                     dep.setHead(Boolean.FALSE);
                  }

                  return adep;
               }
            }

            dep.setHead(Boolean.FALSE);
         }

         // Process arguments
         for (; pair != null; pair = pair.getNext())
         {
            dependency(pair.getHead(), bStatic, dep, local, machine);

            if (!(pair.getTail() instanceof Pair))
            {
               break;
            }
         }
      }

      return null;
   }

   /**
    * Computes the dependency list of an S-expression.
    * @param expr The S-expression.
    * @param bStatic True to look for static dependencies only.
    * @param machine The VM from where to get and expand the macros.
    * @return The computed dependency list.
    */
   public final Pair dependency(Object expr, boolean bStatic, Machine machine)
   {
      if (expr instanceof Pair)
      {
         Pair dep = new Pair(Boolean.TRUE);

         dependency(expr, bStatic, dep, null, machine);

         return dep.getNext();
      }

      return null;
   }

   /**
    * Resolves the initializers in this class and its subclasses.
    * @param machine The VM for macro expansion.
    */
   public final void resolveInitializers(Machine machine)
   {
      verifyNotReadOnly();

      sortInitializedAttributes(false, machine);
      m_initializedStaticAttributeArray = sortInitializedAttributes(true, machine);

      for (int i = 0, nCount = getDerivedCount(); i < nCount; ++i)
      {
         getDerived(i).resolveInitializers(machine);
      }
   }

   /**
    * Sorts topologically the attributes with initializers.
    * @param bStatic True to sort the static attributes, false to sort the instance attributes.
    * @param machine The VM for macro expansion.
    * @return The array of initialized attributes, sorted in the order of their dependency. Can be null.
    */
   private final Attribute[] sortInitializedAttributes(boolean bStatic, Machine machine)
   {
      List attributeList = (bStatic) ? m_staticAttributeList : m_instanceAttributeList;

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

      List noPredList = null;
      Pair[] depArray = null;
      int[] nCountArray = null;
      int nCount = attributeList.size();
      int nInitCount = 0;

      for (int i = 0; i < nCount; ++i)
      {
         Attribute attribute = (Attribute)attributeList.get(i);

         if (attribute.isStatic() == bStatic && attribute.getInitializer() != Undefined.VALUE)
         {
            if (nInitCount++ == 0)
            {
               noPredList = new ArrayList();
               depArray = new Pair[nCount];
               nCountArray = new int[nCount];
            }

            for (Pair dep = dependency(attribute.getInitializer(), bStatic, machine);
               dep != null; dep = dep.getNext())
            {
               Attribute src = (Attribute)((dep.getHead() instanceof Pair) ?
                  ((Pair)dep.getHead()).getHead() : dep.getHead());

               if (src.isStatic() == bStatic && src.getInitializer() != Undefined.VALUE)
               {
                  depArray[src.getOrdinal()] = new Pair(attribute, depArray[src.getOrdinal()]);
                  ++nCountArray[attribute.getOrdinal()];
               }
            }
         }
      }

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

      for (int i = 0; i < nCount; ++i)
      {
         Attribute attribute = (Attribute)attributeList.get(i);

         if (attribute.isStatic() == bStatic && attribute.getInitializer() != Undefined.VALUE)
         {
            if (nCountArray[attribute.getOrdinal()] == 0)
            {
               noPredList.add(attribute);
            }
         }
      }

      Attribute[] initArray = new Attribute[nInitCount];
      int nLastInit = 0;

      for (int i = 0; i < noPredList.size(); ++i)
      {
         Attribute attribute = (Attribute)noPredList.get(i);

         initArray[nLastInit++] = attribute;

         for (Pair dep = depArray[attribute.getOrdinal()]; dep != null; dep = dep.getNext())
         {
            attribute = (Attribute)dep.getHead();

            if (--nCountArray[attribute.getOrdinal()] == 0)
            {
               noPredList.add(attribute);
            }
         }
      }

      if (nLastInit != nInitCount)
      {
         for (int i = 0; i < nCountArray.length; ++i)
         {
            if (nCountArray[i] != 0)
            {
               throw new MetadataException("err.meta.attributeDepCycle",
                  new Object[]{((Attribute)attributeList.get(i)).getName(), getName()});
            }
         }
      }

      return initArray;
   }

   /**
    * @see nexj.core.meta.Accessor#getMetaclass()
    */
   public final Metaclass getMetaclass()
   {
      return this;
   }

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

   /**
    * @see nexj.core.meta.Accessor#isLazy()
    */
   public final boolean isLazy()
   {
      return false;
   }

   /**
    * @see nexj.core.meta.Accessor#getValue(int)
    */
   public final Object getValue(int nOrdinal)
   {
      Object[] valueArray = getState();
      Object value = valueArray[nOrdinal];

      if (value instanceof Undefined)
      {
         Attribute attribute = getStaticAttribute(nOrdinal);

         if (attribute.getValueFunction() != null)
         {
            value = ThreadContextHolder.getContext().getMachine()
               .invoke(attribute.getValueFunction(), this, (Object[])null);
         }
         else
         {
            value = null;
         }

         if (attribute.isCached())
         {
            valueArray[nOrdinal] = value;
         }
      }

      return value;
   }

   /**
    * @see nexj.core.meta.Accessor#getValue(java.lang.String)
    */
   public final Object getValue(String sName)
   {
      Attribute attribute = getAttribute(sName);

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

      throw new ScriptingException("err.scripting.staticAttribute",
         new Object[]{attribute.getName(), getName()});
   }

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

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

         attribute = getAttribute(sFallbackName);
      }

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

      throw new ScriptingException("err.scripting.staticAttribute",
         new Object[]{attribute.getName(), getName()});
   }

   /**
    * @see nexj.core.meta.Accessor#setValue(int, java.lang.Object)
    */
   public final void setValue(int nOrdinal, Object value)
   {
      Object[] valueArray = getState();

      if (valueArray[nOrdinal] != value)
      {
         Attribute attribute = getStaticAttribute(nOrdinal);

         // Check the access rights

         Context context = ThreadContextHolder.getContext();

         if (context.isSecure())
         {
            if (!attribute.isUpdateable(context.getPrivilegeSet()))
            {
               throw new SecurityViolationException(
                  (attribute.isReadOnly()) ?
                     "err.runtime.attributeReadOnlyAccess" :
                     "err.runtime.attributeUpdateAccess",
                  new Object[]{attribute.getName(), getName()});
            }

            Attribute accessAttribute = attribute.getAccessAttribute();

            if (accessAttribute != null)
            {
               Object accessValue = getValue(accessAttribute.getOrdinal());

               if (accessValue instanceof Boolean && !((Boolean)accessValue).booleanValue())
               {
                  throw new SecurityViolationException("err.runtime.attributeUpdateAccess",
                     new Object[]{attribute.getName(), getName()});
               }
            }
         }
         else
         {
            if (attribute.isReadOnly())
            {
               throw new SecurityViolationException("err.runtime.attributeReadOnlyAccess",
                  new Object[]{attribute.getName(), getName()});
            }
         }

         attribute.invalidateDependency(this, Invalid.VALUE);
         valueArray[nOrdinal] = value;
      }
   }

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

      if (attribute.isStatic())
      {
         setValue(attribute.getOrdinal(), value);
      }
      else
      {
         throw new ScriptingException("err.scripting.staticAttribute",
            new Object[]{attribute.getName(), getName()});
      }
   }

   /**
    * @see nexj.core.meta.Accessor#getValueDirect(int)
    */
   public final Object getValueDirect(int nOrdinal)
   {
      return getState()[nOrdinal];
   }

   /**
    * @see nexj.core.meta.Accessor#setValueDirect(int, java.lang.Object)
    */
   public final void setValueDirect(int nOrdinal, Object value)
   {
      getState()[nOrdinal] = value;
   }

   /**
    * @see nexj.core.meta.Accessor#invalidate(int, Object)
    */
   public final void invalidate(int nOrdinal, Object value)
   {
      getState()[nOrdinal] = value;
   }

   /**
    * @return The class object state (with lazy construction).
    */
   private final Object[] getState()
   {
      Machine machine = ThreadContextHolder.getContext().getMachine();
      GlobalEnvironment env = machine.getGlobalEnvironment();
      Object[] values = (Object[])env.getState(this);

      if (values == null)
      {
         values = new Object[getStaticAttributeCount()];
         Arrays.fill(values, Undefined.VALUE);
         env.setState(this, values);

         int nCount = getInitializedStaticAttributeCount();

         if (nCount != 0)
         {
            for (int i = 0; i < nCount; ++i)
            {
               Attribute attribute = getInitializedStaticAttribute(i);

               values[attribute.getOrdinal()] = machine.invoke(attribute.getInitializerFunction(), this, (Object[])null);
            }
         }
      }

      return values;
   }

   /**
    * @see nexj.core.meta.Accessor#invoke(java.lang.String, java.lang.Object[])
    */
   public final Object invoke(String sName, Object[] args)
   {
      Event event = (Event)getSelector(sName).getMember((args != null) ? args.length : 0);

      if (!event.isStatic())
      {
         throw new ScriptingException("err.scripting.staticEvent", new Object[]{sName, getName()});
      }

      return event.invoke(this, args, ThreadContextHolder.getContext().getMachine());
   }

   /**
    * @see nexj.core.meta.Accessor#invoke(java.lang.String, nexj.core.scripting.Pair)
    */
   public final Object invoke(String sName, Pair args)
   {
      Event event = (Event)getSelector(sName).getMember(Pair.length(args));

      if (!event.isStatic())
      {
         throw new ScriptingException("err.scripting.staticEvent", new Object[]{sName, getName()});
      }

      return event.invoke(this, args, ThreadContextHolder.getContext().getMachine());
   }

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

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

         if (sym instanceof Symbol)
         {
            Selector selector = findSelector(((Symbol)sym).getName());

            if (selector == null)
            {
               try
               {
                  return machine.invokeJavaMethod(this, nArgCount);
               }
               catch (ScriptingException e)
               {
                  if ("err.scripting.unknownMethod".equals(e.getErrorCode()))
                  {
                     throw new MetadataLookupException("err.meta.selectorLookup", sym.toString(), this);
                  }

                  throw e;
               }
            }

            Member member = selector.getMember(nArgCount - 1);

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

                     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
               {
                  machine.setArg(0, nArgCount, this);

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

                  return ((Event)member).getFunction().invoke(nArgCount, machine);
               }
            }
            else
            {
               try
               {
                  return machine.invokeJavaMethod(this, nArgCount);
               }
               catch (ScriptingException e)
               {
                  if ("err.scripting.unknownMethod".equals(e.getErrorCode()))
                  {
                     throw new ScriptingException(
                        (member.isAttribute()) ?
                           "err.scripting.staticAttribute" :
                           "err.scripting.staticEvent",
                        new Object[]{sym, getName()});
                  }

                  throw e;
               }
            }
         }
      }
      else
      {
         throw new ScriptingException("err.scripting.minArgCount",
            new Object[]{getName(),
               Primitive.ONE_INTEGER,
               Primitive.createInteger(nArgCount)});
      }

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

   /**
    * @see nexj.core.meta.MetadataObject#makeReadOnly()
    */
   public final void makeReadOnly()
   {
      for (Iterator itr = getAttributeIterator(); itr.hasNext();)
      {
         ((MetadataObject)itr.next()).makeReadOnly();
      }

      for (Iterator itr = getEventIterator(); itr.hasNext();)
      {
         ((MetadataObject)itr.next()).makeReadOnly();
      }

      for (Iterator itr = getSelectorIterator(); itr.hasNext();)
      {
         ((MetadataObject)itr.next()).makeReadOnly();
      }

      if (m_persistenceMapping != null)
      {
         m_persistenceMapping.makeReadOnly();
      }

      super.makeReadOnly();

      if (m_derivedList instanceof ArrayList)
      {
         ((ArrayList)m_derivedList).trimToSize();
      }

      if (m_eventList instanceof ArrayList)
      {
         ((ArrayList)m_eventList).trimToSize();
      }

      if (m_instanceAttributeList instanceof ArrayList)
      {
         ((ArrayList)m_instanceAttributeList).trimToSize();
      }

      if (m_persistenceAliasList instanceof ArrayList)
      {
         ((ArrayList)m_persistenceAliasList).trimToSize();
      }

      if (m_staticAttributeList instanceof ArrayList)
      {
         ((ArrayList)m_staticAttributeList).trimToSize();
      }

      if (m_validationFunction instanceof ArrayList)
      {
         ((ArrayList)m_validationFunction).trimToSize();
      }

      m_textPosMap = null; // free memory not used after compile()
   }

   /**
    * @return True if the class is readable according to the visibility and the privilege set.
    */
   public final boolean isReadable(PrivilegeSet privilegeSet)
   {
      return m_nVisibility == PUBLIC &&
         (m_readPrivilege == null || privilegeSet.contains(m_readPrivilege));
   }

   /**
    * Checks the class 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 final void checkReadAccess(PrivilegeSet privilegeSet) throws SecurityViolationException
   {
      if (m_nVisibility != PUBLIC)
      {
         throw new SecurityViolationException("err.rpc.classVisibility", new Object[]{getName()});
      }

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

   /**
    * Verifies that the read access to all the attributes of the class is granted.
    * @param attributes The attribute list to check.
    * @param privilegeSet The privilege set of the current principal.
    * @return List of security attributes that can be appended to the input attributes.
    * @throws SecurityViolationException the access is not granted to any of the attributes.
    */
   public Pair checkReadAccess(Pair attributes, PrivilegeSet privilegeSet) throws SecurityViolationException
   {
      Attribute readAccessAttribute = m_readAccessAttribute;
      Pair security = null;

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

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

            if (head == Symbol.ATAT)
            {
               pair = pair.getNext();
               head = pair.getHead();

               pair = m_metadata.getMetaclass(((Symbol)head).getName())
                  .checkReadAccess(pair.getNext(), privilegeSet);

               if (pair != null)
               {
                  security = new Pair(new Pair(Symbol.ATAT, new Pair(head, pair)), security);
               }

               continue;
            }
         }

         Attribute attribute = findAttribute((Symbol)head);

         if (attribute != null)
         {
            if (attribute == readAccessAttribute)
            {
               readAccessAttribute = null;
            }

            attribute.checkReadAccess(privilegeSet);

            if (!attribute.getType().isPrimitive())
            {
               pair = ((Metaclass)attribute.getType())
                  .checkReadAccess((pair == null) ? null : pair.getNext(), privilegeSet);

               if (pair != null)
               {
                  security = new Pair(new Pair (head, pair), security);
               }
            }
         }
      }

      if (readAccessAttribute != null)
      {
         security = new Pair(readAccessAttribute.getSymbol(), security);
      }

      return security;
   }

   /**
    * Checks the read access of an order by clause.
    * @param orderBy The order by clause.
    * @param privilegeSet The privilege set of the current principal.
    * @throws SecurityViolationException the access is not granted to any of the attributes.
    */
   public void checkOrderByAccess(Object orderBy, PrivilegeSet privilegeSet)
   {
      Pair pair;

      for (; orderBy instanceof Pair; orderBy = pair.getTail())
      {
         pair = (Pair)orderBy;

         if (pair.getHead() instanceof Pair)
         {
            checkExpressionAccess(((Pair)pair.getHead()).getHead(), privilegeSet);
         }
      }
   }

   /**
    * Checks the read access of an expression.
    * @param expr The expression.
    * @param privilegeSet The privilege set of the current principal.
    * @throws SecurityViolationException the access is not granted to any of the attributes.
    */
   public void checkExpressionAccess(Object expr, PrivilegeSet privilegeSet)
   {
      Metaclass metaclass = this;

      if (expr instanceof Pair)
      {
         Pair pair = (Pair)expr;
         Object head = pair.getHead();

         expr = pair.getTail();

         if (head instanceof Symbol)
         {
            if (head == Symbol.ATAT && expr instanceof Pair)
            {
               pair = (Pair)expr;
               head = pair.getHead();

               if (!(head instanceof Symbol))
               {
                  return;
               }

               metaclass = m_metadata.findMetaclass(head.toString());

               if (metaclass == null)
               {
                  return;
               }

               metaclass.checkReadAccess(privilegeSet);

               head = Symbol.AT;
               expr = pair.getTail();
            }

            if (head == Symbol.AT)
            {
               for (; expr instanceof Pair; expr = pair.getTail())
               {
                  pair = (Pair)expr;
                  head = pair.getHead();

                  if (head instanceof Pair)
                  {
                     metaclass.checkExpressionAccess(head, privilegeSet);
                     metaclass = metaclass.getDerived(head);

                     continue;
                  }

                  if (!(head instanceof Symbol))
                  {
                     break;
                  }

                  Attribute attribute = metaclass.findAttribute((Symbol)head);

                  if (attribute == null)
                  {
                     break;
                  }

                  attribute.checkReadAccess(privilegeSet);

                  if (attribute.getType().isPrimitive())
                  {
                     break;
                  }

                  metaclass = (Metaclass)attribute.getType();
               }

               return;
            }

            if (expr instanceof Pair)
            {
               Metaclass clazz = m_metadata.findMetaclass(head.toString());

               if (clazz != null)
               {
                  if (m_nVisibility != PUBLIC)
                  {
                     throw new SecurityViolationException("err.rpc.classVisibility", new Object[]{clazz.getName()});
                  }

                  pair = (Pair)expr;

                  if (pair.getHead() instanceof Pair)
                  {
                     int nArgCount = 0;
                     Object next;

                     for (next = pair.getTail(); next instanceof Pair; next = ((Pair)next).getTail())
                     {
                        ++nArgCount;
                     }

                     if (next == null)
                     {
                        pair = (Pair)pair.getHead();

                        if (pair.getHead() == Symbol.QUOTE && pair.getTail() instanceof Pair)
                        {
                           pair = pair.getNext();

                           if (pair.getHead() instanceof Symbol && pair.getTail() == null)
                           {
                              Member member = clazz.getSelector(pair.getHead().toString()).getMember(nArgCount);

                              if (!member.isStatic())
                              {
                                 throw new ScriptingException(
                                    (member.isAttribute()) ?
                                       "err.scripting.staticAttribute" :
                                       "err.scripting.staticEvent",
                                    new Object[]{member.getName(), clazz.getName()});
                              }

                              if (member.isAttribute())
                              {
                                 if (nArgCount == 0)
                                 {
                                    ((Attribute)member).checkReadAccess(privilegeSet);
                                 }
                                 else
                                 {
                                    ((Attribute)member).checkUpdateAccess(privilegeSet);
                                 }
                              }
                              else
                              {
                                 ((Event)member).checkAccess(privilegeSet);
                              }

                              expr = ((Pair)expr).getTail();
                              head = null;
                           }
                        }
                     }
                  }
               }
            }

            if (head != null && !m_metadata.isPublicSymbol((Symbol)head))
            {
               throw new SecurityViolationException("err.rpc.functionVisibility",
                  new Object[]{head});
            }
         }
         else if (head instanceof Pair)
         {
            throw new SecurityViolationException("err.rpc.expressionVisibility",
               new Object[]{head});
         }

         for (; expr instanceof Pair; expr = pair.getTail())
         {
            pair = (Pair)expr;
            metaclass.checkExpressionAccess(pair.getHead(), privilegeSet);
         }
      }
      else if (expr instanceof Symbol)
      {
         Attribute attribute  = metaclass.findAttribute((Symbol)expr);

         if (attribute != null)
         {
            attribute.checkReadAccess(privilegeSet);
         }
      }
   }

   /**
    * Gets the most derived class object according to a filter.
    * @param filter The filter expression. Can be null.
    * @return The most derived class object (can be this).
    */
   public Metaclass getDerived(Object filter)
   {
      Metaclass base = this;

      if (filter instanceof Pair)
      {
         Pair pair = (Pair)filter;
         Object head = pair.getHead();

         if (head == Symbol.AND)
         {
            // (and ...)
            for (pair = pair.getNext(); pair != null; pair = pair.getNext())
            {
               base = base.getDerived(pair.getHead());
            }
         }
         else if (head == Symbol.INSTANCE_P)
         {
            // (instance? (@) <class>)
            Metaclass metaclass = base.getCastMetaclass(pair);

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

      return base;
   }

   /**
    * Gets a class object from an (instance? (@) class) expression.
    * @param expr The instance? expression.
    * @return The class object or null if the expression is not (instance? (@) class).
    */
   public Metaclass getCastMetaclass(Pair expr) throws MetadataException
   {
      if (expr.getHead() == Symbol.INSTANCE_P)
      {
         expr = expr.getNext();

         if (expr != null)
         {
            Object head = expr.getHead();

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

               if (assoc.getHead() == Symbol.AT && assoc.getTail() == null)
               {
                  expr = expr.getNext();

                  if (expr != null && expr.getTail() == null)
                  {
                     head = expr.getHead();

                     if (head instanceof Symbol)
                     {
                        Metaclass metaclass = m_metadata.getMetaclass(head.toString());

                        if (metaclass.isUpcast(this))
                        {
                           return this;
                        }

                        return metaclass;
                     }
                  }
               }
            }
         }
      }

      return null;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#findAttributeMeta(java.lang.String)
    */
   public final AttributeMeta findAttributeMeta(String sName)
   {
      return findAttribute(sName);
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#findDerivedClassMeta(java.lang.String)
    */
   public final ClassMeta findDerivedClassMeta(String sDerivedName)
   {
      return findDerivedMetaclass(sDerivedName);
   }

   /**
    * Find a derived metaclass.
    * @param sDerivedName The name of the derived Metaclass.
    * @return The Metaclass; null if not found.
    */
   public final Metaclass findDerivedMetaclass(String sDerivedName)
   {
      Metaclass derived = m_metadata.findMetaclass(sDerivedName);

      if (derived != null && derived.getBase() == this)
      {
         return derived;
      }

      return null;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getAttributeMeta(java.lang.String)
    */
   public final AttributeMeta getAttributeMeta(String sName)
   {
      return getAttribute(sName);
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getAttributeMetaCount()
    */
   public final int getAttributeMetaCount()
   {
      return m_attributeMap.size();
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getAttributeMetaIterator()
    */
   public final Iterator getAttributeMetaIterator()
   {
      return m_attributeMap.valueIterator();
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getBaseClassMeta()
    */
   public final ClassMeta getBaseClassMeta()
   {
      return m_base;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getCreatePrivilegeOrdinal()
    */
   public final int getCreatePrivilegeOrdinal()
   {
      if (m_createPrivilege != null)
      {
         return m_createPrivilege.getOrdinal();
      }

      return -1;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getDeleteAccessAttributeMeta()
    */
   public final AttributeMeta getDeleteAccessAttributeMeta()
   {
      return m_deleteAccessAttribute;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getDeletePrivilegeOrdinal()
    */
   public final int getDeletePrivilegeOrdinal()
   {
      if (m_deletePrivilege != null)
      {
         return m_deletePrivilege.getOrdinal();
      }

      return -1;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getDerivedAttributeMetaCount()
    */
   public final int getDerivedAttributeMetaCount()
   {
      return getDerivedCount();
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getDerivedAttributeMetaIterator()
    */
   public final Iterator getDerivedAttributeMetaIterator()
   {
      return getDerivedIterator();
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getDerivedClassMeta(java.lang.String)
    */
   public final ClassMeta getDerivedClassMeta(String sName)
   {
      ClassMeta derived = findDerivedClassMeta(sName);

      if (derived == null)
      {
         throw new MetadataLookupException("err.meta.derivedClassLookup", sName, this);
      }

      return derived;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getLockingAttributeMeta()
    */
   public final AttributeMeta getLockingAttributeMeta()
   {
      return (m_persistenceMapping == null) ? null : m_persistenceMapping.getLockingAttribute();
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getNameAttributeMeta()
    */
   public final AttributeMeta getNameAttributeMeta()
   {
      return m_nameAttribute;
   }

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

      return -1;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getUpdateAccessAttributeMeta()
    */
   public final AttributeMeta getUpdateAccessAttributeMeta()
   {
      return m_updateAccessAttribute;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getUpdatePrivilegeOrdinal()
    */
   public final int getUpdatePrivilegeOrdinal()
   {
      if (m_updatePrivilege != null)
      {
         return m_updatePrivilege.getOrdinal();
      }

      return -1;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#isUpcast(nexj.core.meta.ui.ClassMeta)
    */
   public final boolean isUpcast(ClassMeta metaclass)
   {
      while (metaclass != null)
      {
         if (metaclass == this)
         {
            return true;
         }

         metaclass = metaclass.getBaseClassMeta();
      }

      return false;
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getInstanceAttributeMeta(int)
    */
   public AttributeMeta getInstanceAttributeMeta(int nOrdinal)
   {
      return getInstanceAttribute(nOrdinal);
   }

   /**
    * @see nexj.core.meta.ui.ClassMeta#getStaticAttributeMeta(int)
    */
   public AttributeMeta getStaticAttributeMeta(int nOrdinal)
   {
      return getStaticAttribute(nOrdinal);
   }
  
   /**
    * Sets the pointcut flag.
    * @param bPointcut The pointcut flag.
    */
   public final void setPointcut(boolean bPointcut)
   {
      verifyNotReadOnly();
      m_bPointcut = bPointcut;
   }

   /**
    * @see nexj.core.meta.Pointcut#isPointcut()
    */
   public final boolean isPointcut()
   {
      return m_bPointcut;
   }

   /**
    * @see nexj.core.meta.Pointcut#addAspect(nexj.core.meta.Aspect)
    */
   public final void addAspect(Aspect aspect) throws MetadataException
   {
      verifyNotReadOnly();

      if (m_pointcutHelper == null)
      {
         m_pointcutHelper = new ClassPointcutHelper(this);
      }

      m_pointcutHelper.addAspect(aspect);
   }

   /**
    * @see nexj.core.meta.Pointcut#removeAspect(nexj.core.meta.Aspect)
    */
   public final boolean removeAspect(Aspect aspect)
   {
      if (m_pointcutHelper != null)
      {
         return m_pointcutHelper.removeAspect(aspect);
      }

      return false;
   }

   /**
    * @see nexj.core.meta.Pointcut#getAspect(int)
    */
   public final Aspect getAspect(int nOrdinal)
   {
      return m_pointcutHelper.getAspect(nOrdinal);
   }

   /**
    * @see nexj.core.meta.Pointcut#hasAspect(nexj.core.meta.Aspect)
    */
   public final boolean hasAspect(Aspect aspect)
   {
      if (m_pointcutHelper == null)
      {
         return false;
      }

      return m_pointcutHelper.hasAspect(aspect);
   }

   /**
    * @see nexj.core.meta.Pointcut#getAspectCount()
    */
   public final int getAspectCount()
   {
      if (m_pointcutHelper == null)
      {
         return 0;
      }

      return m_pointcutHelper.getAspectCount();
   }

   /**
    * @return The direct aspect count - direct aspects have indexes [0..nCount-1].
    */
   public final int getDirectAspectCount()
   {
      return m_nDirectAspectCount;
   }

   /**
    * @see nexj.core.meta.Pointcut#addAspectOverride(nexj.core.meta.Aspect, boolean)
    */
   public final void addAspectOverride(Aspect aspect, boolean bInclusive) throws MetadataException
   {
      verifyNotReadOnly();

      if (m_pointcutHelper == null)
      {
         m_pointcutHelper = new ClassPointcutHelper(this);
      }

      m_pointcutHelper.addAspectOverride(aspect, bInclusive);
   }

   /**
    * @see nexj.core.meta.Pointcut#removeAspectOverride(nexj.core.meta.Aspect)
    */
   public final boolean removeAspectOverride(Aspect aspect)
   {
      if (m_pointcutHelper != null)
      {
         return m_pointcutHelper.removeAspectOverride(aspect);
      }

      return false;
   }

   /**
    * @see nexj.core.meta.Pointcut#findAspectOverride(nexj.core.meta.Aspect)
    */
   public final int findAspectOverride(Aspect aspect)
   {
      if (m_pointcutHelper == null)
      {
         return -1;
      }

      return m_pointcutHelper.findAspectOverride(aspect);
   }

   /**
    * @see nexj.core.meta.Pointcut#getAspectOverride(int)
    */
   public final Aspect getAspectOverride(int nOrdinal)
   {
      return m_pointcutHelper.getAspectOverride(nOrdinal);
   }

   /**
    * @see nexj.core.meta.Pointcut#isAspectOverrideInclusive(int)
    */
   public final boolean isAspectOverrideInclusive(int nOrdinal)
   {
      return m_pointcutHelper.isAspectOverrideInclusive(nOrdinal);
   }

   /**
    * @see nexj.core.meta.Pointcut#getAspectOverrideCount()
    */
   public final int getAspectOverrideCount()
   {
      if (m_pointcutHelper == null)
      {
         return 0;
      }

      return m_pointcutHelper.getAspectOverrideCount();
   }

   /**
    * @see nexj.core.meta.MetadataObject#setProperties(nexj.core.meta.MetadataMarker)
    */
   public void setProperties(MetadataMarker marker)
   {
      marker.setResourceName(m_sResourceName);
      marker.setTypeName("Class");
      marker.setProperty("class", m_sName);
   }

   /**
    * @see nexj.core.meta.NamedMetadataObject#createLookupException()
    */
   protected MetadataException createLookupException()
   {
      return new MetadataLookupException("err.meta.metaclassLookup", m_sName, m_metadata.getName());
   }

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

      if (m_persistenceMapping != null)
      {
         m_persistenceMapping.validate(metadata, warnings);
      }
   }

   /**
    * Creates an compatibility conflict exception.
    * @param sErrCode The error string identifier.
    * @param argArray The error string arguments.
    */
   public MetadataValidationException createCompatibilityException(String sErrCode, Object[] argArray)
   {
      MetadataValidationException e = new MetadataCompatibilityException(sErrCode, argArray);

      e.setTypeName("Class");
      e.setResourceName(m_sResourceName);
      e.setProperty("class", m_sName);

      return e;
   }

   /**
    * Checks whether a metaclass is compatible with this instance.
    * @param metaclass The metaclass. Can be null if no corresponding metaclass exists.
    * @param eh The holder in which to add exceptions.
    */
   public void checkCompatibility(Metaclass metaclass, ExceptionHolder eh)
   {
      if (checkCompatibility(getAttributeIterator(), metaclass, eh))
      {
         checkCompatibility(getEventIterator(), metaclass, eh);
      }
   }

   /**
    * Checks compatibility over an iteration of members.
    * @param memberItr Iterator over members.
    * @param metaclass The metaclass. Can be null if no corresponding metaclass exists.
    * @param eh The holder in which to add exceptions.
    * @return false if there exists a compatible member but metaclass is null.
    */
   public boolean checkCompatibility(Iterator memberItr, Metaclass metaclass, ExceptionHolder eh)
   {
      while (memberItr.hasNext())
      {
         Member compatibleMember = (Member)memberItr.next();

         if (compatibleMember.isCompatible())
         {
            if (metaclass == null)
            {
               eh.addException(createCompatibilityException("err.meta.missingCompatibleClass", new Object[] {getName()}));
               return false;
            }

            compatibleMember.checkCompatibility(metaclass, eh);
         }
      }

      return true;
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#getChild(java.lang.String)
    */
   public EndpointPart getChild(String sName)
   {
      if (!StringUtil.isEmpty(sName) && sName.charAt(0) == ':')
      {
         if (sName.equals(":oid"))
         {
            return Transformation.OID;
         }

         if (sName.equals(":class"))
         {
            return Transformation.CLASS;
         }

         if (sName.equals(":event"))
         {
            return Transformation.EVENT;
         }
      }

      return getAttribute(sName);
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#findChild(java.lang.String)
    */
   public EndpointPart findChild(String sName)
   {
      if (!StringUtil.isEmpty(sName) && sName.charAt(0) == ':')
      {
         if (sName.equals(":oid"))
         {
            return Transformation.OID;
         }

         if (sName.equals(":class"))
         {
            return Transformation.CLASS;
         }

         if (sName.equals(":event"))
         {
            return Transformation.EVENT;
         }
      }

      return findAttribute(sName);
   }

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

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

   /**
    * @see nexj.core.meta.integration.EndpointPart#getBaseEndpoint()
    */
   public TransformationEndpoint getBaseEndpoint()
   {
      return m_base;
   }

   /**
    * @see nexj.core.meta.integration.TransformationEndpoint#isUpcast(nexj.core.meta.integration.TransformationEndpoint)
    */
   public boolean isUpcast(TransformationEndpoint endpoint)
   {
      return (endpoint instanceof Metaclass) && isUpcast((Metaclass)endpoint);
   }

   /**
    * @see nexj.core.meta.integration.TransformationEndpoint#getEndpoint(java.lang.String)
    */
   public TransformationEndpoint getEndpoint(String sName)
   {
      return m_metadata.getMetaclass(sName);
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#createObject()
    */
   public TransferObject createObject()
   {
      return new TransferObject(getName());
   }

   /**
    * @see nexj.core.meta.integration.EndpointPart#getValue(nexj.core.util.PropertyMap, java.lang.Object)
    */
   public Object getValue(PropertyMap map, Object defValue)
   {
      throw new IllegalStateException();
   }

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

   // inner classes

   /**
    * Metaclass-specific pointcut helper.
    */
   protected final static class ClassPointcutHelper extends PointcutHelper
   {
      protected Metaclass m_metaclass;

      public ClassPointcutHelper(Metaclass metaclass)
      {
         m_metaclass = metaclass;
      }

      /**
       * @see nexj.core.meta.PointcutHelper#getContainer()
       */
      protected Pointcut getContainer()
      {
         return m_metaclass;
      }

      /**
       * @see nexj.core.meta.PointcutHelper#getContainerType()
       */
      protected String getContainerType()
      {
         return "class";
      }
   }

   /**
    * Interface implemented by attribute visitors.
    */
   protected interface AttributeVisitor
   {
      /**
       * Visits the specified attribute.
       * @param attribute The attribute to visit.
       */
      void visit(Attribute attribute);
   }

   /**
    * Attribute visitor for dependency computation.
    */
   protected abstract static class AttributeDependencyVisitor implements AttributeVisitor
   {
      public final void visit(Attribute attribute)
      {
         if (attribute.getDeclarator() == attribute.getMetaclass() &&
            (attribute.getValue() != Undefined.VALUE ||
               attribute.getDependency() != null ||
               attribute.getOrderBy() != null))
         {
            computeDependency(attribute);
         }
      }

      protected abstract void computeDependency(Attribute attribute);
   }

   /**
    * Represents a local variable dependency binding.
    */
   protected static class Local
   {
      // associations

      /**
       * The variable symbol.
       */
      public Symbol symbol;

      /**
       * The dependency list.
       */
      public Pair dep;

      /**
       * The next binding in the list.
       */
      public Local next;

      // constructors

      /**
       * Constructs the local variable binding.
       * @param symbol The variable name.
       * @param dep The dependency list. Can be null.
       * @param next The next binding in the list. Can be null.
       */
      public Local(Symbol symbol, Pair dep, Local next)
      {
         this.symbol = symbol;
         this.dep = dep;
         this.next = next;
      }

      // operations

      /**
       * Finds a binding by variable symbol.
       * @param symbol The variable symbol.
       * @param first The first binding in the linked list. Can be null.
       * @return The found binding, or null if not found.
       */
      public static Local find(Symbol symbol, Local first)
      {
         while (first != null && first.symbol != symbol)
         {
            first = first.next;
         }

         return first;
      }
   }
}
TOP

Related Classes of nexj.core.meta.Metaclass$Local

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.