Package nexj.core.meta.integration.format.object

Source Code of nexj.core.meta.integration.format.object.ObjectMessagePartMapping

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

import java.util.Set;

import nexj.core.meta.Attribute;
import nexj.core.meta.ContextMetadata;
import nexj.core.meta.Metaclass;
import nexj.core.meta.MetadataException;
import nexj.core.meta.MetadataMarker;
import nexj.core.meta.MetadataObject;
import nexj.core.meta.MetadataValidationException;
import nexj.core.meta.Primitive;
import nexj.core.meta.integration.CompositeMessagePart;
import nexj.core.meta.integration.CompositeMessagePartInstance;
import nexj.core.meta.integration.CompositeMessagePartRef;
import nexj.core.meta.integration.Message;
import nexj.core.meta.integration.MessagePart;
import nexj.core.meta.integration.MessagePartMapping;
import nexj.core.meta.integration.PrimitiveMessagePart;
import nexj.core.scripting.Pair;
import nexj.core.scripting.Symbol;
import nexj.core.util.ExceptionHolder;
import nexj.core.util.HashHolder;
import nexj.core.util.HashTab;
import nexj.core.util.Lookup;
import nexj.core.util.ObjUtil;

/**
* Object message part mapping.
*/
public class ObjectMessagePartMapping extends MetadataObject implements MessagePartMapping
{
   // constants

   /**
    * System attribute value for TransferObject class
    */
   public final static byte ATTR_CLASS = 0;

   /**
    * System attribute value for TransferObject event
    */
   public final static byte ATTR_EVENT = 1;

   /**
    * System attribute value for TransferObject object ID
    */
   public final static byte ATTR_OID = 2;

   /**
    * System attribute value for external key for the synchronized TransferObject
    */
   public final static byte ATTR_SYNC_KEY = 3;

   /**
    * System attribute value for external URL (if supported) for the synchronized TransferObject
    */
   public final static byte ATTR_SYNC_URL = 4;

   /**
    * System attribute value for external group ID (if supported) for the synchronized TransferObject.
    * Group ID is used by an external system to link different instances together
    * (e.g. appointment instances in multiple Exchange mailboxes, corresponding to the same meeting).
    */
   public final static byte ATTR_SYNC_GROUP = 5;

   /**
    * System attribute value for the version or locking attribute (if supported) of the synchronized TransferObject.
    */
   public final static byte ATTR_SYNC_VERSION = 6;

   /**
    * Ordered values of system attributes of TransferObject
    */
   protected final static String[] SYSTEM_ATTRIBUTES = new String[]
   {
      ":class",
      ":event",
      ":oid",
      ":sync-key",
      ":sync-url",
      ":sync-group",
      ":sync-version",
   };

   // attributes

   /**
    * The association code. Can be null.
    */
   protected String m_sAssociationCode;

   /**
    * Index in the SYSTEM_ATTRIBUTES list of the name of the part system attribute (like :oid or :sync-key); -1 for regular attributes
    */
   protected byte m_nSystemAttribute = -1;

   /**
    * The key flag.
    * True if the value is used as lookup key
    * for the parent part instance.
    */
   protected boolean m_bKey;

   /**
    * The subkey flag.
    * True if the value is used as a lookup key
    * for the parent part instance, which in turn is
    * a lookup key for the grand-parent part instance.
    */
   protected boolean m_bSubKey;

   /**
    * The subkey parent.
    * True if there is a child message part, which is a subkey.
    */
   protected boolean m_bSubKeyParent;

   /**
    * The local key flag.
    * True to lookup the instance only within
    * the subcollection scoped by the parent part instance.
    */
   protected boolean m_bLocal = true;
  
   /**
    * The create-if-missing flag.
    * If true, a missing instance will be created.
    * If false, a failed instance lookup is an error.
    */
   protected boolean m_bCreate = true;

   /**
    * The update-if-found flag.
    * If false, the underlying attribute (specified by m_attribute)
    * will not be updated on an already existing instance.
    */
   protected boolean m_bUpdate = true;
  
   /**
    * The delete-if-not-found flag.
    * If true, all unmatched instances of the underlying collection attribute
    * (specified by m_attribute) will be deleted.
    */
   protected boolean m_bDelete;
  
   /**
    * The truncate flag.
    * If true, the underlying attribute value will be truncated to fit within the maximum length.
    */
   protected boolean m_bTruncated;
  
   /**
    * The dependency list created flag.
    * If true, the dependency list for the message part has been created.
    */
   protected boolean m_bDependencyMapCreated;

   /**
    * The primary flag.
    * Used to disambiguate the selection of a message when more than one message in
    * the inheritance hierarchy is mapped to the same class.
    */
   protected boolean m_bPrimary;

   /**
    * True, if the object mapping has another key (not subkey) defined besides the :sync-key.
    */
   protected boolean m_bAlternativeSyncKey;

   // associations
  
   /**
    * The part class.
    * Specifies the class to which the part maps.
    */
   protected Metaclass m_metaclass;

   /**
    * The part attribute.
    * Specifies the attribute to which the part maps.
    * Null for system attributes (like :oid or :sync-key)
    */
   protected Attribute m_attribute;
  
   /**
    * The update access attribute.
    * If its value is false, the update to the attribute (specified by m_attribute)
    * or the whole instance (in root part) will be skipped.
    */
   protected Attribute m_accessAttribute;

   /**
    * The where clause for instance lookup.
    * Can be used only with a composite message parts.
    */
   protected Object m_where = Boolean.TRUE;

   /**
    * The child message part corresponding to the "updatable" attribute. Can be null.
    */
   protected MessagePart m_updateAccessPart;

   /**
    * The child message parts corresponding to the system attributes. Can be null.
    */
   protected MessagePart[] m_systemPartArray;
  
   /**
    * The map containing association paths indexed by dependent message parts.
    */
   protected Lookup m_assocPathByDependentMsgPartMap;

   /**
    * Map of metaclass to message (that can be up-cast to this mapping's message).
    * Valid only on mapping associated with root node. Message[Metaclass]
    */
   protected Lookup m_classMessageMap;

   /**
    * The message with which this mapping is associated.
    */
   protected Message m_message;

   /**
    * The map containing association paths indexed by association code.
    */
   protected Lookup m_assocPathByAssocCodeMap;

   // operations

   /**
    * Sets the part class.
    * @param metaclass The part class to set.
    */
   public void setMetaclass(Metaclass metaclass)
   {
      verifyNotReadOnly();
      m_metaclass = metaclass;
   }

   /**
    * @return The part class.
    */
   public Metaclass getMetaclass()
   {
      return m_metaclass;
   }

   /**
    * Sets the part attribute and update access attributes. Must be called after inheritance
    * has been resolved so that the attributes can be set correctly on the derived messages as well.
    *
    * @param sAttributeName The attribute name; can be null.
    * @param sAccessAttributeName The update access attribute name; can be null.
    * @param part The message part.
    * @param message The message.
    */
   public void setAttributes(String sAttributeName, String sAccessAttributeName, MessagePart part, Message message)
   {
      setAttribute(sAttributeName, part);
      setAccessAttribute(sAccessAttributeName);

      String[] sPartNameArray = null;

      for (int i = 0, nCount = message.getDerivedMessageCount(); i < nCount; i++)
      {
         Message derivedMessage = message.getDerivedMessage(i);

         if (sPartNameArray == null)
         {
            int nSize = 0;

            for (MessagePart parent = part; parent.getParent() != null; parent = parent.getParent())
            {
               nSize++;
            }

            sPartNameArray = new String[nSize];

            for (int k = nSize - 1; k >= 0; k--)
            {
               sPartNameArray[k] = part.getName();
               part = part.getParent();
            }
         }

         if (sPartNameArray != null)
         {
            part = derivedMessage.getRoot();

            for (int k = 0; k < sPartNameArray.length; k++)
            {
               part = ((CompositeMessagePart)part).getPart(sPartNameArray[k]);
            }

            ObjectMessagePartMapping mapping = (ObjectMessagePartMapping)part.getMapping();

            mapping.setAttributes(sAttributeName, sAccessAttributeName, part, derivedMessage);
         }
      }
   }

   /**
    * Sets the part attribute.
    * @param sName The attribute name. Can be null.
    * @param part The message part.
    */
   public void setAttribute(String sName, MessagePart part)
   {
      verifyNotReadOnly();

      if (sName == null && part.getParent() != null)
      {
         sName = part.getName();
      }

      if (sName != null)
      {
         if (part.getParent() == null)
         {
            throw new MetadataException("err.meta.integration.object.mapping.misplacedAttribute");
         }

         if (sName.length() > 0 && sName.charAt(0) == ':')
         {
            for (byte i = 0; i < SYSTEM_ATTRIBUTES.length; ++i)
            {
               if (SYSTEM_ATTRIBUTES[i].equals(sName))
               {
                  m_nSystemAttribute = i;

                  return;
               }
            }
         }

         ObjectMessagePartMapping mapping = (ObjectMessagePartMapping)part.getParent().getMapping();

         if (mapping != null && mapping.getMetaclass() != null)
         {
            m_attribute = mapping.getMetaclass().getAttribute(sName);

            if (m_metaclass == null && !m_attribute.getType().isPrimitive())
            {
               m_metaclass = (Metaclass)m_attribute.getType();
            }
         }
      }
   }

   /**
    * @return One of the ATTR_* constants.
    */
   public byte getSystemAttribute()
   {
      return m_nSystemAttribute;
   }

   /**
    * @return The system attribute name or null.
    */
   public String getSystemAttributeName()
   {
      return (m_nSystemAttribute >= 0) ? (String)SYSTEM_ATTRIBUTES[m_nSystemAttribute] : null;
   }

   /**
    * @return The part attribute.
    */
   public Attribute getAttribute()
   {
      return m_attribute;
   }

   /**
    * Sets the key flag.
    * @param bKey The key flag to set.
    */
   public void setKey(boolean bKey)
   {
      verifyNotReadOnly();
      m_bKey = bKey;

      if (m_bKey)
      {
         m_bLocal = false;
      }
   }

   /**
    * @return The key flag.
    */
   public boolean isKey()
   {
      return m_bKey;
   }

   /**
    * Sets the subkey flag.
    * @param bSubKey The subkey flag to set.
    */
   public void setSubKey(boolean bSubKey)
   {
      verifyNotReadOnly();
      m_bSubKey = bSubKey;
   }

   /**
    * @return The subkey flag.
    */
   public boolean isSubKey()
   {
      return m_bSubKey;
   }

   /**
    * @return The subkey parent.
    */
   public boolean isSubKeyParent()
   {
      return m_bSubKeyParent;
   }
  
   /**
    * Sets the local key flag.
    * @param bLocal The local key flag to set.
    */
   public void setLocal(boolean bLocal)
   {
      verifyNotReadOnly();
      m_bLocal = bLocal;
   }

   /**
    * @return The local key flag.
    */
   public boolean isLocal()
   {
      return m_bLocal;
   }

   /**
    * Sets the create-if-missing flag.
    * @param bCreate The create-if-missing flag to set.
    */
   public void setCreate(boolean bCreate)
   {
      verifyNotReadOnly();
      m_bCreate = bCreate;
   }

   /**
    * @return The create-if-missing flag.
    */
   public boolean isCreate()
   {
      return m_bCreate;
   }
  
   /**
    * Sets the update-if-found flag.
    * @param bUpdate The update-if-found flag to set.
    */
   public void setUpdate(boolean bUpdate)
   {
      verifyNotReadOnly();
      m_bUpdate = bUpdate;
   }

   /**
    * @return The update-if-found flag.
    */
   public boolean isUpdate()
   {
      return m_bUpdate;
   }

   /**
    * @return The delete-if-not-found flag.
    */
   public boolean isDelete()
   {
      return m_bDelete;
   }
  
   /**
    * Sets the delete-if-not-found flag.
    * @param bDelete The delete-if-not-found flag to set.
    */
   public void setDelete(boolean bDelete)
   {
      verifyNotReadOnly();
      m_bDelete = bDelete;
   }

   /**
    * Sets the truncate flag.
    * @param bTruncated The truncate flag. to set.
    */
   public void setTruncated(boolean bTruncated)
   {
      verifyNotReadOnly();
      m_bTruncated = bTruncated;
   }

   /**
    * @return The truncate flag.
    */
   public boolean isTruncated()
   {
      return m_bTruncated;
   }

   /**
    * Sets the primary flag. Used to disambiguate the selection of a message when more
    * than one message in the inheritance hierarchy is mapped to the same class.
    * @param bPrimary The value of the primary flag.
    */
   public void setPrimary(boolean bPrimary)
   {
      verifyNotReadOnly();
      m_bPrimary = bPrimary;
   }

   /**
    * Gets the primary flag. Used to disambiguate the selection of a message when more
    * than one message in the inheritance hierarchy is mapped to the same class.
    * @return The value of the primary flag.
    */
   public boolean isPrimary()
   {
      return m_bPrimary;
   }

   /**
    * Sets the update access attribute.
    * @param sName The attribute name. Can be null.
    */
   public void setAccessAttribute(String sName)
   {
      verifyNotReadOnly();

      if (sName == null)
      {
         m_accessAttribute = null;
      }
      else
      {
         Metaclass metaclass;

         if (m_attribute != null)
         {
            metaclass = m_attribute.getMetaclass();
         }
         else
         {
            metaclass = m_metaclass;
         }

         if (metaclass != null)
         {
            m_accessAttribute = metaclass.getAttribute(sName);
         }
      }
   }

   /**
    * Sets the update access attribute.
    * @param accessAttribute The update access attribute to set.
    */
   public void setAccessAttribute(Attribute accessAttribute)
   {
      verifyNotReadOnly();
      m_accessAttribute = accessAttribute;
   }

   /**
    * @return The update access attribute.
    */
   public Attribute getAccessAttribute()
   {
      return m_accessAttribute;
   }

   /**
    * Sets the child message part corresponding to the "updatable" attribute.
    * @param attrib The child message part corresponding to the "updatable" attribute to set. Can be null.
    */
   public void setUpdateAccessPart(MessagePart part)
   {
      verifyNotReadOnly();
      m_updateAccessPart = part;
   }

   /**
    * @return The child message part corresponding to the "updatable" attribute. Can be null.
    */
   public MessagePart getUpdateAccessPart()
   {
      return m_updateAccessPart;
   }

   /**
    * Sets the association code.
    * @param sValue The association code.
    */
   public void setAssociationCode(String sValue)
   {
      verifyNotReadOnly();
      m_sAssociationCode = sValue;
   }

   /**
    * @return The association code. Can be null.
    */
   public String getAssociationCode()
   {
      return m_sAssociationCode;
   }

   /**
    * Sets a child message part corresponding to a system attribute.
    * @param part The child message part to set. Can be a non-system part, in which case it is ignored.
    * @throws MetadataException if the mapping is duplicate.
    */
   public void setSystemPart(MessagePart part)
   {
      byte nAttr = ((ObjectMessagePartMapping)part.getMapping()).getSystemAttribute();

      if (nAttr >= 0)
      {
         if (m_systemPartArray == null)
         {
            m_systemPartArray = new MessagePart[SYSTEM_ATTRIBUTES.length];
         }

         if (m_systemPartArray[nAttr] != null && part != m_systemPartArray[nAttr])
         {
            throw new MetadataException("err.meta.integration.object.mapping.systemMappingDup",
               new Object[]{SYSTEM_ATTRIBUTES[nAttr], part.getFullPath()});
         }

         m_systemPartArray[nAttr] = part;
      }
   }

   /**
    * Gets the child message part mapped to a given system attribute.
    * @param nAttr One of the ATTR_* constants.
    * @return The child message part or null.
    */
   public MessagePart getSystemPart(int nAttr)
   {
      if (m_systemPartArray != null)
      {
         return m_systemPartArray[nAttr];
      }

      return null;
   }

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

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

   /**
    * Gets the message with which this mapping is associated.
    * @return The message of this mapping.
    */
   public Message getMessage()
   {
      return m_message;
   }

   /**
    * @return True, if the object mapping has another key (not subkey) defined besides the :sync-key.
    */
   public boolean hasAlternativeSyncKey()
   {
      return m_bAlternativeSyncKey;
   }

   /**
    * Adds a mapping from a metaclass to its message.
    * @param clazz The class.
    * @param mappedMessage The message to which the class maps.
    */
   private void addClassMessage(Metaclass clazz, Message mappedMessage)
   {
      if (m_classMessageMap == null)
      {
         m_classMessageMap = new HashTab();
      }

      Message other = (Message)m_classMessageMap.put(clazz, mappedMessage);
      Message baseMessage;

      if (other == null)
      {
         // No collision, add mapping to parent as well.
         if ((baseMessage = m_message.getBaseMessage()) != null)
         {
            ((ObjectMessagePartMapping)baseMessage.getRoot().getMapping()).addClassMessage(clazz, mappedMessage);
         }
      }
      else
      {
         // If old entry is base of new entry, new entry is left in map: it is more specific.
         // Otherwise, update map with a more general entry.
         if (!other.isUpcast(mappedMessage))
         {
            if (!mappedMessage.isUpcast(other))
            {
               // The old entry and new entry do not inherit from each other.
               ObjectMessagePartMapping mappedMessageMapping = (ObjectMessagePartMapping)other.getRoot().getMapping();
               ObjectMessagePartMapping otherMapping = (ObjectMessagePartMapping)other.getRoot().getMapping();

               if (otherMapping.isPrimary() ^ mappedMessageMapping.isPrimary())
               {
                  if (otherMapping.isPrimary())
                  {
                     // The old entry takes precedence over the new entry.
                     m_classMessageMap.put(clazz, mappedMessage = other);
                  }
               }
               else
               {
                  // Unable to disambiguate; map to their common base.
                  assert m_message.isUpcast(other) && m_message.isUpcast(mappedMessage);
                  m_classMessageMap.put(clazz, mappedMessage = m_message);
               }
            }

            // Add the mapping to our parent as well.
            if ((baseMessage = m_message.getBaseMessage()) != null)
            {
               ((ObjectMessagePartMapping)baseMessage.getRoot().getMapping()).addClassMessage(clazz, mappedMessage);
            }
         }
      }
   }

   /**
    * Finds a message that can be up-cast to the message associated with this mapping and
    * that best represents the given metaclass (i.e. most-strictly encloses the metaclass).
    * @param clazz The metaclass.
    * @return The message that most-strictly encloses the metaclass.
    */
   public Message findMessage(Metaclass clazz)
   {
      if (m_classMessageMap == null)
      {
         return (m_metaclass == clazz) ? m_message : null;
      }

      return (Message)m_classMessageMap.get(clazz);
   }

   /**
    * Build a list of all attributes included in the given message part.
    * @param composite The composite message part.
    * @return list of all attributes included in the message
    */
   public static Pair getAttributes(CompositeMessagePart composite)
   {
      ObjectMessagePartMapping mapping = (ObjectMessagePartMapping)composite.getMapping();
      Set currPathSet = new HashHolder();     // keeps track of cycle(s) in messages

      currPathSet.add(composite);

      return (Pair)mapping.getAttributes(composite, currPathSet, null);
   }

   /**
    * Build a list of all attributes included in the given message part and its descendants.
    * @param part The message part.
    * @param currPathSet The current path.
    * @return The list of attributes or an attribute symbol.
    */
   private Object getAttributes(MessagePart part, Set currPathSet, Message message)
   {
      CompositeMessagePart refPart = null;
      ObjectMessagePartMapping partMapping = (ObjectMessagePartMapping)part.getMapping();

      if (part instanceof PrimitiveMessagePart)
      {
         if (message != null && part.getDeclarator() == message && partMapping.getMessage() != message)
         {
            return null;
         }

         return (m_attribute == null) ? null : m_attribute.getSymbol();
      }
      else if (part instanceof CompositeMessagePartRef)
      {
         refPart = ((CompositeMessagePartRef)part).getRefPart();
         message = refPart.getDeclarator();

         if (!currPathSet.add(refPart))
         {
            return (m_attribute == null) ? null : new Pair(m_attribute.getSymbol());
         }
      }

      CompositeMessagePart composite = (CompositeMessagePart)part;
      Pair attributes = null;

      for (int i = composite.getPartCount() - 1; i >= 0; --i)
      {
         MessagePart childPart = composite.getPart(i);
         ObjectMessagePartMapping childMapping = (ObjectMessagePartMapping)childPart.getMapping();
         Object childAttributes = childMapping.getAttributes(childPart, currPathSet, message);

         if (childAttributes != null)
         {
            attributes = new Pair(childAttributes, attributes);
         }
      }

      // Add attributes from derived messages
      if (refPart != null)
      {
         ObjectMessagePartMapping refMapping = (ObjectMessagePartMapping)refPart.getMapping();
         Message refMessage = refMapping.getMessage();

         for (int i = 0, nCount = refMessage.getDerivedMessageCount(); i < nCount; i++)
         {
            Message derivedMessage = refMessage.getDerivedMessage(i);
            CompositeMessagePart derivedRoot = derivedMessage.getRoot();
            ObjectMessagePartMapping derivedMapping = (ObjectMessagePartMapping)derivedRoot.getMapping();
            Object derivedAttributes = derivedMapping.getAttributes(derivedRoot, currPathSet, refMessage);

            if (derivedAttributes != null)
            {
               if (derivedAttributes instanceof Symbol)
               {
                  derivedAttributes = new Pair(derivedAttributes);
               }

               if (derivedMapping.getMetaclass() != refMapping.getMetaclass())
               {
                  derivedAttributes = new Pair(Symbol.ATAT,
                     new Pair(derivedMapping.getMetaclass().getSymbol(), derivedAttributes));
                  attributes = new Pair(derivedAttributes, attributes);
               }
               else
               {
                  attributes = Pair.nconc((Pair)derivedAttributes, attributes);
               }
            }
         }

         currPathSet.remove(refPart);
      }

      return (m_attribute == null) ? attributes : new Pair(m_attribute.getSymbol(), attributes);
   }

   /**
    * Computes the shortest association code path, one of which is a suffix of the other,
    * which prefix refers to its own beginning, i.e. makes a full cycle.
    * @param associationPath The association path.
    * @param oldPath The existing association path.
    * @return The shortest association path or null if no such path can be found.
    */
   private Pair findCommonAssocSuffix(Pair associationPath, Pair oldPath)
   {
      int nDiffLen = Pair.length(associationPath) - Pair.length(oldPath);
      Pair longerPath = (nDiffLen > 0) ? associationPath : oldPath;

      if (nDiffLen != 0)
      {
         CompositeMessagePart rootPart = ((CompositeMessagePart)longerPath.getHead()).getRoot();

         for (int i = Math.abs(nDiffLen); i > 1; i--, longerPath = longerPath.getNext()) ;

         CompositeMessagePart recursivePart = (CompositeMessagePart)longerPath.getHead();

         if (recursivePart instanceof CompositeMessagePartRef)
         {
            CompositeMessagePart refPart = ((CompositeMessagePartRef)recursivePart).getRefPart();

            if (refPart != rootPart)
            {
               return null;
            }
         }

         longerPath = longerPath.getNext();
      }

      if (!ObjUtil.equal((nDiffLen <= 0) ? associationPath : oldPath, longerPath))
      {
         return null;
      }

      return longerPath;
   }

   /**
    * Adds a dependent message part and its corresponding association path to the map.
    * @param part The dependent message part.
    * @param associationPath The association path.
    * @param bRoot True if root message part.
    * @throws MetadataValidationException if the association code is not unique.
    */
   private void addAssociationPath(MessagePart part, Pair associationPath, boolean bRoot)
   {
      if (associationPath != null)
      {
         Pair pathToAdd = associationPath;

         if (m_assocPathByDependentMsgPartMap == null)
         {
            m_assocPathByDependentMsgPartMap = new HashTab();
         }
         else
         {
            Pair oldPath = (Pair)m_assocPathByDependentMsgPartMap.get(part);

            if (oldPath != null)
            {
               pathToAdd = findCommonAssocSuffix(associationPath, oldPath);
            }
            else if (m_assocPathByAssocCodeMap != null)
            {
               ObjectMessagePartMapping partMapping = (ObjectMessagePartMapping)part.getMapping();

               if (m_assocPathByAssocCodeMap.get(partMapping.getAssociationCode()) != null)
               {
                  pathToAdd = null;
               }
            }

            if (pathToAdd == null)
            {
               ObjectMessagePartMapping partMapping = (ObjectMessagePartMapping)part.getMapping();

               throw new MetadataValidationException("err.meta.integration.object.mapping.associationCodeDup",
                  new Object[]{partMapping.getAssociationCode(), part.getFullPath()});
            }
         }

         m_assocPathByDependentMsgPartMap.put(part, pathToAdd);

         if (bRoot)
         {
            if (m_assocPathByAssocCodeMap == null)
            {
               m_assocPathByAssocCodeMap = new HashTab();
            }

            m_assocPathByAssocCodeMap.put(
               ((ObjectMessagePartMapping)part.getMapping()).getAssociationCode(), pathToAdd);
         }
      }
   }

   /**
    * @return The reverse association path for the dependentPart.
    * @param dependentPart The dependent message part.
    */
   public Pair getReverseAssociationPath(MessagePart dependentPart)
   {
      Pair associationPath = (dependentPart != null && m_assocPathByDependentMsgPartMap != null) ?
         (Pair)m_assocPathByDependentMsgPartMap.get(dependentPart) : null;

      Pair reverseAssocPath = null;

      if (associationPath != null)
      {
         for (; associationPath != null; associationPath = associationPath.getNext())
         {
            Symbol partSymbol = ((ObjectMessagePartMapping)((MessagePart)associationPath.getHead()).getMapping())
               .getAttribute().getSymbol();

            reverseAssocPath = new Pair(partSymbol, reverseAssocPath);
         }

         reverseAssocPath = new Pair(Symbol.ATAT, new Pair(getMetaclass().getSymbol(),
            Pair.nreverse(reverseAssocPath)));
      }

      return reverseAssocPath;
   }

   /**
    * @return The dependent message parts count.
    */
   public int getDependentMessagePartsCount()
   {
      return (m_assocPathByDependentMsgPartMap != null) ? m_assocPathByDependentMsgPartMap.size() : 0;
   }

   /**
    * @return The dependent message parts iterator.
    */
   public Lookup.Iterator getDependentMessagePartsIterator()
   {
      return (m_assocPathByDependentMsgPartMap != null) ? m_assocPathByDependentMsgPartMap.iterator() : null;
   }

   /**
    * Builds a map of dependent message parts and their corresponding association paths.
    * @param composite The composite message part.
    */
   protected void buildDependentMsgPartsMap(CompositeMessagePart composite)
   {
      if (!m_bDependencyMapCreated)
      {
         Set currPathSet = new HashHolder();

         currPathSet.add(composite);
         buildDependentMsgPartsMap(composite, currPathSet);
         m_bDependencyMapCreated = true;
      }
   }

   /**
    * @return The association path corresponding to the association code.
    */
   public Pair getAssociationPath(Object associationCode)
   {
      return (associationCode != null && m_assocPathByAssocCodeMap != null) ?
         (Pair)m_assocPathByAssocCodeMap.get(associationCode) :
         null;
   }

   /**
    * Builds a map of dependent message parts and their association paths. If the referenced message contains
    * a non-null value for the associationCode attribute, add this part's mapping to its parent's dependency
    * map along with the association path. This process continues until root is reached.
    */
   protected void buildDependentMsgPartsMap(CompositeMessagePart composite, Set currPathSet)
   {
      for (int i = 0, n = composite.getPartCount(); i != n; ++i)
      {
         MessagePart msgPart = composite.getPart(i);

         if (msgPart instanceof CompositeMessagePart)
         {
            CompositeMessagePart part = (CompositeMessagePart)msgPart;
            ObjectMessagePartMapping partMapping = (ObjectMessagePartMapping)part.getMapping();

            if (partMapping.getAssociationCode() != null)
            {
               Pair associationPath = new Pair(part);
               CompositeMessagePart rootPart = part.getRoot();

               for (MessagePart parentMsgPart = part.getParent(); parentMsgPart != null; parentMsgPart = parentMsgPart.getParent())
               {
                  ObjectMessagePartMapping parentMsgPartMapping = (ObjectMessagePartMapping)parentMsgPart.getMapping();
                  boolean bRoot = (parentMsgPart == rootPart);

                  if (parentMsgPartMapping.isDelete() || bRoot)
                  {
                     parentMsgPartMapping.addAssociationPath(part, associationPath, bRoot);
                  }

                  if (parentMsgPartMapping.getAttribute() != null)
                  {
                     associationPath = new Pair(parentMsgPart, associationPath);
                  }
               }
            }

            if (currPathSet.add(part))
            {
               buildDependentMsgPartsMap(part, currPathSet);
               currPathSet.remove(part);
            }
         }
      }
   }

   /**
    * @see nexj.core.meta.integration.MessagePartMapping#init(nexj.core.meta.integration.MessagePart)
    */
   public void init(MessagePart part)
   {
      m_message = part.getRoot().getDeclarator();
   }

   /**
    * @see nexj.core.meta.integration.MessagePartMapping#resolveInheritance(nexj.core.meta.integration.MessagePartMapping)
    */
   public void resolveInheritance(MessagePartMapping baseMapping)
   {
   }

   /**
    * @see nexj.core.meta.integration.MessagePartMapping#refer(CompositeMessagePartRef)
    */
   public void refer(CompositeMessagePartRef ref)
   {
      ObjectMessagePartMapping mapping = (ObjectMessagePartMapping)ref.getRefPart().getMapping();
      CompositeMessagePart rootPart = ref.getRoot();
      ObjectMessagePartMapping rootMapping = (ObjectMessagePartMapping)rootPart.getMapping();

      // Build the dependent message parts map for the root message.
      if (rootMapping != null)
      {
         rootMapping.buildDependentMsgPartsMap(rootPart);
      }

      if (mapping != this)
      {
         if (mapping.m_systemPartArray != null)
         {
            for (int i = 0; i < SYSTEM_ATTRIBUTES.length; ++i)
            {
               MessagePart part = mapping.getSystemPart(i);

               if (part != null)
               {
                  setSystemPart(part);
               }
            }
         }

         // Build the dependent message parts map for the referenced message.
         mapping.buildDependentMsgPartsMap(ref.getRefPart());

         Pair localAssocPath = null;

         // Copy the dependency list from the referenced part to the referee while appending to the association paths.
         for (CompositeMessagePart partToResolve = ref; partToResolve != null; partToResolve = partToResolve.getParent())
         {
            ObjectMessagePartMapping partParentMapping = (ObjectMessagePartMapping)partToResolve.getMapping();

            if (mapping.getDependentMessagePartsCount() > 0)
            {
               for (Lookup.Iterator itr = mapping.getDependentMessagePartsIterator(); itr.hasNext();)
               {
                  CompositeMessagePart part = (CompositeMessagePart)itr.next();
                  boolean bRoot = (partToResolve == rootPart);

                  // skip itself, however add to root even if the delete flag is not set on it
                  if (partToResolve != part && (partParentMapping.isDelete() || bRoot))
                  {
                     partParentMapping.addAssociationPath(part, Pair.append(localAssocPath, (Pair)itr.getValue()),
                        bRoot);
                  }
               }
            }

            if (partParentMapping != null && partParentMapping.getAttribute() != null)   // skip root
            {
               localAssocPath = new Pair(partToResolve, localAssocPath);
            }
         }
      }
   }

   /**
    * @see nexj.core.meta.integration.MessagePartMapping#finish(nexj.core.meta.integration.MessagePart)
    */
   public void finish(MessagePart part)
   {
      MessagePart parent = part.getParent();

      if (m_attribute != null)
      {
         if (m_attribute.isStatic())
         {
            throw new MetadataException("err.meta.integration.object.mapping.staticAttribute",
               new Object[]{part.getFullPath()});
         }

         if ((part instanceof PrimitiveMessagePart) != m_attribute.getType().isPrimitive())
         {
            throw new MetadataException("err.meta.integration.object.mapping.attributeTypeMismatch",
               new Object[]{part.getFullPath()});
         }

         if (m_bLocal && part instanceof CompositeMessagePart && m_attribute.getReverse() == null)
         {
            throw new MetadataException("err.meta.integration.object.mapping.missingLocalReverseAssoc",
               new Object[]{m_attribute.getName(), part.getFullPath()});
         }
      }

      if (m_metaclass != null)
      {
         if (part instanceof PrimitiveMessagePart)
         {
            throw new MetadataException("err.meta.integration.object.mapping.misplacedClass");
         }
        
         if (m_attribute != null && !((Metaclass)m_attribute.getType()).isUpcast(m_metaclass))
         {
            throw new MetadataException("err.meta.integration.object.mapping.classTypeMismatch",
               new Object[]{m_metaclass.getName()});
         }
      }
      else
      {
         if (parent == null)
         {
            if (m_message.getDerivation() != Message.DERIVATION_ABSTRACT)
            {
               throw new MetadataException("err.meta.integration.object.mapping.missingClass");
            }
         }
        
         if (m_attribute != null && !m_attribute.getType().isPrimitive())
         {
            m_metaclass = (Metaclass)m_attribute.getType();
         }
      }

      if (part instanceof PrimitiveMessagePart)
      {
         if (part.isCollection())
         {
            throw new MetadataException("err.meta.integration.object.mapping.primitiveCollection");
         }

         if (m_where != Boolean.TRUE)
         {
            throw new MetadataException("err.meta.integration.object.mapping.primitivePartWhere",
               new Object[]{part.getFullPath()});
         }
      }

      if (parent == null)
      {
         m_bLocal = false;
      }
      else
      {
         ObjectMessagePartMapping mapping = (ObjectMessagePartMapping)parent.getMapping();

         if (mapping != null)
         {
            mapping.setSystemPart(part);
         }
      }

      if (m_bSubKey)
      {
         ObjectMessagePartMapping mapping = (parent == null) ? null : (ObjectMessagePartMapping)parent.getMapping();

         if (mapping != null)
         {
            mapping.m_bSubKeyParent = true;
         }
      }

      if (m_accessAttribute != null && m_accessAttribute.getType() != Primitive.BOOLEAN)
      {
         throw new MetadataException("err.meta.integration.object.mapping.accessAttributeType",
            new Object[]{m_accessAttribute.getName()});
      }

      if (parent == null && m_metaclass != null &&
         (m_message.getBaseMessage() != null || m_message.getDerivedMessageCount() > 0))
      {
         addClassMessage(m_metaclass, m_message);
      }

      if (part instanceof CompositeMessagePartInstance)
      {
         CompositeMessagePart composite = (CompositeMessagePartInstance)part;

         for (int i = 0, nCount = composite.getPartCount(); i < nCount; i++)
         {
            MessagePart child = composite.getPart(i);
            MessagePartMapping mapping = child.getMapping();

            if (mapping != null)
            {
               mapping.finish(child);
            }
         }
      }

      if (part.equals(part.getRoot()) && part instanceof CompositeMessagePart)
      {
         CompositeMessagePart rootComposite = (CompositeMessagePart)part;
         ObjectMessagePartMapping rootMapping = (ObjectMessagePartMapping)rootComposite.getMapping();
         MessagePart syncKeyPart = rootMapping.getSystemPart(ATTR_SYNC_KEY);

         if (syncKeyPart != null)
         {
            for (int i = 0, nCount = rootComposite.getPartCount(); i < nCount; i++)
            {
               MessagePart childPart = rootComposite.getPart(i);

               if (syncKeyPart != childPart)
               {
                  ObjectMessagePartMapping childMapping = (ObjectMessagePartMapping)childPart.getMapping();

                  if (childMapping.isKey())
                  {
                     rootMapping.m_bAlternativeSyncKey = true;
                  }
               }
            }
         }
      }
   }

   /**
    * Resolves attributes that could not be determined during the original
    * initialization due to incomplete metadata.
    */
   public void finish2(MessagePart part)
   {
      if (part instanceof PrimitiveMessagePart)
      {
         return;
      }

      CompositeMessagePart composite = (CompositeMessagePart)part;

      for (int i = composite.getPartCount() - 1; i >= 0; --i)
      {
         MessagePart childPart = composite.getPart(i);
         ObjectMessagePartMapping partMapping = (ObjectMessagePartMapping)childPart.getMapping();

         if (partMapping.getAttribute() != null &&
            partMapping.getAttribute() == m_metaclass.getUpdateAccessAttribute())
         {
            setUpdateAccessPart(childPart);

            break;
         }
      }

      if (composite instanceof CompositeMessagePartInstance)
      {
         for (int i = 0, nSize = composite.getPartCount(); i < nSize; i++)
         {
            MessagePart child = composite.getPart(i);

            ((ObjectMessagePartMapping)child.getMapping()).finish2(child);
         }
      }
   }

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

      copy.m_assocPathByDependentMsgPartMap = null;
      copy.m_bDependencyMapCreated = false;
      copy.m_bSubKeyParent = false;
      copy.m_updateAccessPart = null;
      copy.m_systemPartArray = null;

      return copy;
   }

   /**
    * @see nexj.core.meta.integration.MessagePartMapping#validate(nexj.core.meta.ContextMetadata, nexj.core.util.ExceptionHolder, nexj.core.meta.integration.MessagePart)
    */
   public void validate(ContextMetadata metadata, ExceptionHolder warnings, MessagePart part)
   {
      if (warnings != null)
      {
         MessagePart parent = part.getParent();
         ObjectMessagePartMapping mapping = (parent == null) ? null : (ObjectMessagePartMapping)parent.getMapping();

         if (m_bSubKey && (mapping == null || !mapping.isKey() || !mapping.isLocal()))
         {
            MetadataValidationException e = new MetadataValidationException("err.meta.integration.object.mapping.nonKeyLocalSubKey");

            setProperties(e, part);
            warnings.addException(e);
         }

         if (m_bKey && part.isCollection() && !m_bLocal)
         {
            MetadataValidationException e = new MetadataValidationException("err.meta.integration.object.mapping.collectionKeyNeedsSubKey");

            setProperties(e, part);
            warnings.addException(e);
         }

         if (m_bKey && m_bLocal && !m_bSubKeyParent)
         {
            MetadataValidationException e = new MetadataValidationException("err.meta.integration.object.mapping.localKeyNeedsSubKey");

            setProperties(e, part);
            warnings.addException(e);
         }
      }
   }

   /**
    * Sets the metadata marker properties.
    * @param marker The destination marker.
    * @param part The message part
    */
   public void setProperties(MetadataMarker marker, MessagePart part)
   {
      marker.setTypeName("Message");
      marker.setProperty("part", part.getName());
      marker.setProperty("message", part.getRoot().getName());
   }
}
TOP

Related Classes of nexj.core.meta.integration.format.object.ObjectMessagePartMapping

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.