Package nexj.core.integration

Source Code of nexj.core.integration.Transformer

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

import java.util.ArrayList;
import java.util.List;

import nexj.core.integration.io.ObjectInput;
import nexj.core.integration.io.ObjectOutput;
import nexj.core.meta.Primitive;
import nexj.core.meta.integration.CompositeMessagePartRef;
import nexj.core.meta.integration.Message;
import nexj.core.meta.integration.Transformation;
import nexj.core.meta.integration.TransformationArgument;
import nexj.core.meta.integration.TransformationEndpoint;
import nexj.core.meta.integration.TransformationMapping;
import nexj.core.meta.integration.TransformationSource;
import nexj.core.meta.integration.EndpointPart;
import nexj.core.rpc.TransferObject;
import nexj.core.runtime.InvocationContext;
import nexj.core.runtime.InvocationContextAware;
import nexj.core.scripting.Function;
import nexj.core.scripting.Pair;
import nexj.core.util.HashTab;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.PropertyMap;
import nexj.core.util.StringUtil;
import nexj.core.util.Undefined;

/**
* Transforms data in a PropertyMap to a TransferObject. The source and destination structures are
* described by TransformationEndpoints, and the mapping of data is described by a Transformation.
*/
public class Transformer implements InvocationContextAware
{
   // constants
  
   /**
    * Source array element size.
    */
   protected final static int SOURCE_SIZE = 3;

   // attributes

   /**
    * True if this Transformer is polymorphic.
    */
   protected boolean m_bPolymorphic = true;
  
   // associations
  
   /**
    * The invocation context.
    */
   protected InvocationContext m_context;

   /**
    * The output data.
    */
   protected TransferObject m_output;

   /**
    * The source object list: Object[3*n], List[3*n+1], Integer[3*n+2]
    */
   protected Object[] m_sourceArray;
  
   /**
    * Maps a combination of source and destination lists and a source index
    * to a resulting transfer object: TransferObject[ListKey].
    */
   protected Lookup m_listMap;
  
   /**
    * The list key.
    */
   protected ListKey m_listKey;

   /**
    * Array of mapping functions, indexed by mapping ordinal number.
    */
   protected Object[] m_scriptArray;

   /**
    * The argument value list for script invocation.
    */
   protected List m_argList = new ArrayList(4);

   /**
    * A cache of message parsers. MessageParser[Format].
    */
   protected Lookup m_parserMap;

   /**
    * A cache of message formatters. MessageFormatter[Format].
    */
   protected Lookup m_formatterMap;

   // constructors

   /**
    * Constructs the transformer.
    * @param context The invocation context.
    */
   public Transformer(InvocationContext context)
   {
      m_context = context;
   }
  
   /**
    * Constructs the transformer.
    */
   public Transformer()
   {
      super();
   }

   // operations

   /**
    * Set whether this Transformer is polymorphic or not.
    * @param bPolymorphic True if this Transformer should be polymorphic, false otherwise.
    */
   public void setPolymorphic(boolean bPolymorphic)
   {
      m_bPolymorphic = bPolymorphic;
   }

   /**
    * Get whether this Transformer is polymorphic or not.
    * @return True if this Transformer should be polymorphic, false otherwise.
    */
   public boolean isPolymorphic()
   {
      return m_bPolymorphic;
   }
  
   /**
    * @see nexj.core.runtime.InvocationContextAware#setInvocationContext(nexj.core.runtime.InvocationContext)
    */
   public void setInvocationContext(InvocationContext context)
   {
      m_context = context;
   }

   /**
    * Transforms a message (without passing any arguments to the transformation).
    * @param obj The source message.
    * @param transformation The transformation metadata.
    * @return The transformed message.
    * @throws IntegrationException if a transformation error occurs.
    */
   public TransferObject transform(PropertyMap obj, Transformation transformation) throws IntegrationException
   {
      return transform(obj, transformation, null);
   }

   /**
    * Transforms a message.
    * @param obj The source message.
    * @param transformation The transformation metadata.
    * @param arguments The transformation parameters.
    * @return The transformed message.
    * @throws IntegrationException If a transformation error occurs.
    */
   public TransferObject transform(PropertyMap obj, Transformation transformation, Pair arguments) throws IntegrationException
   {
      Logger logger = transformation.getLogger();

      m_output = new TransferObject();
      m_listMap = new HashTab();

      try
      {
         if (logger.isDebugEnabled())
         {
            logger.debug("Transforming a message with transformation " + transformation.getName());
            logger.dump(obj);
         }

         Transformation oldTransformation = transformation;

         if (!StringUtil.isEmpty(obj.getClassName()) && m_bPolymorphic)
         {
            TransformationEndpoint derivedEndpoint = transformation.getSource().getEndpoint(obj.getClassName());
            Transformation derivedTransformation = findDerivedTransformation(transformation, derivedEndpoint);

            if (oldTransformation.getDerivation() == Transformation.DERIVATION_FINAL && derivedTransformation != oldTransformation)
            {
               throw new IntegrationException("err.integration.messageTypeMismatch",
                  new Object[] {oldTransformation.toString(), oldTransformation.getSource().toString(), derivedEndpoint.toString()});
            }

            if (derivedTransformation != null && derivedTransformation != oldTransformation)
            {
               transformation = derivedTransformation;

               if (logger.isDebugEnabled())
               {
                  logger.debug("Using derived transformation \"" + derivedTransformation.getName() + "\"");
               }
            }
         }

         if (oldTransformation.getDerivation() == Transformation.DERIVATION_ABSTRACT)
         {
            if (transformation == oldTransformation)
            {
               throw new IntegrationException("err.integration.abstractTransformation",
                  new Object[]{oldTransformation.getName()});
            }
         }

         m_sourceArray = new Object[SOURCE_SIZE * (transformation.getMaxLevel() + 1)];

         if (transformation.getFunction() != null)
         {
            m_argList.clear();
            m_argList.add(this);
            m_argList.add(m_output);
            m_argList.add(obj);

            Pair head = arguments;
            int nArgCount = transformation.getArgumentCount();

            while (head != null)
            {
               nArgCount--;
               m_argList.add(head.getHead());
               head = head.getNext();
            }

            if (nArgCount != 0)
            {
               throw new IntegrationException("err.meta.transformation.argumentCount",
                  new Object[] {Primitive.createInteger(transformation.getArgumentCount()),
                     Primitive.createInteger(Pair.length(arguments))});
            }

            m_scriptArray = (Object[])m_context.getMachine().invoke(transformation.getFunction(), m_argList);
         }
         else
         {
            m_scriptArray = null;
         }

         transform(obj, transformation.getRoot());

         if (m_scriptArray != null)
         {
            m_output = (TransferObject)m_context.getMachine().invoke((Function)m_scriptArray[0], (Pair)null);
         }

         if (m_output.getClassName() == null)
         {
            m_output.setClassName(transformation.getDestination().getName());
         }
      }
      catch (Exception e)
      {
         throw new IntegrationException("err.integration.transformation",
            new Object[]{transformation.getName()}, e);
      }

      if (logger.isDumpEnabled())
      {
         logger.dump("Transformation result:");
         logger.dump(m_output);
      }

      return m_output;
   }

   /**
    * Transforms an object or a collection corresponding to a given transformation source.
    * @param obj The object to transform.
    * @param source The corresponding transformation source.
    */
   protected void transform(Object obj, TransformationSource source)
   {
      if (source.getPart().isCollection())
      {
         if (obj != null && obj != Undefined.VALUE)
         {
            List list = (List)obj;

            setSourceList(source.getLevel(), list);

            for (int i = 0, n = list.size(); i != n; ++i)
            {
               setSourceIndex(source.getLevel(), i);
               transform1(list.get(i), source);
            }
         }
      }
      else
      {
         transform1(obj, source);
      }
   }

   /**
    * Transforms a single object corresponding to a transformation source.
    * @param obj The object to transform.
    * @param source The transformation source.
    */
   protected void transform1(Object obj, TransformationSource source)
   {
      setSourceObject(source.getLevel(), obj);

      for (int nMapping = 0, nMappingCount = source.getMappingCount(); nMapping != nMappingCount; ++nMapping)
      {
         TransformationMapping mapping = source.getMapping(nMapping);

         if (mapping.getDestinationCount() != 0)
         {
            boolean bMissing = false;

            m_argList.clear();

            for (int nArg = 0, nArgCount = mapping.getArgumentCount(); nArg != nArgCount; ++nArg)
            {
               TransformationArgument arg = mapping.getArgument(nArg);
               TransformationSource src = arg.getSource();
               int nStartLevel = arg.getStartLevel();
               Object value = getSourceObject(nStartLevel);
               Object parent = getSourceObject(Math.max(0, nStartLevel - 1));
               int nLevel;

               for (nLevel = nStartLevel; nLevel < src.getLevel() && value != null && value != Undefined.VALUE; ++nLevel)
               {
                  parent = value;
                  value = src.getPath(nLevel + 1).getPart().getValue((PropertyMap)parent, Undefined.VALUE);
               }

               if (value == null)
               {
                  if (nLevel != src.getLevel() || !arg.isNull())
                  {
                     bMissing = true;

                     break;
                  }
               }
               else if (value == Undefined.VALUE)
               {
                  if (nLevel != src.getLevel())
                  {
                     bMissing = true;

                     break;
                  }

                  Function fun = arg.getDefaultValueFunction();

                  if (fun == null)
                  {
                     bMissing = true;

                     break;
                  }

                  if (parent == Undefined.VALUE)
                  {
                     parent = null;
                  }

                  value = m_context.getMachine().invoke(fun, parent, (Object[])null);
               }

               m_argList.add(value);
            }

            if (!bMissing)
            {
               Object value;
              
               if (mapping.getOrdinal() != 0)
               {
                  value = m_context.getMachine().invoke(
                     (Function)m_scriptArray[mapping.getOrdinal()], m_argList);
               }
               else
               {
                  value = m_argList.get(0);
               }

               if (value != Undefined.VALUE)
               {
                  setDestination(mapping, source, value);
               }
            }
         }
      }

      if (obj != null && obj != Undefined.VALUE)
      {
         int nChildCount = source.getChildCount();

         if (nChildCount != 0)
         {
            PropertyMap tobj = (PropertyMap)obj;

            for (int nChild = 0; nChild != nChildCount; ++nChild)
            {
               TransformationSource child = source.getChild(nChild);

               if (child.isPrimary())
               {
                  Object value = child.getPart().getValue(tobj, Undefined.VALUE);

                  if (value == null)
                  {
                     if (child.isNull())
                     {
                        transform(null, child);
                     }
                  }
                  else if (value == Undefined.VALUE)
                  {
                     if (child.isDefault())
                     {
                        transform(value, child);
                     }
                  }
                  else
                  {
                     transform(value, child);
                  }
               }
            }
         }
      }
   }

   /**
    * Assigns a value to a given destination.
    * @param mapping The transformation mapping containing the destination.
    * @param source The transformation source.
    * @param value The value to assign.
    */
   protected void setDestination(TransformationMapping mapping, TransformationSource source, Object value)
   {
      TransferObject tobj = m_output;
      int nList = -1;

      value = convert(value, source, mapping);

      for (int i = 0, n = mapping.getDestinationCount() - 1; i <= n; ++i)
      {
         EndpointPart destinationPart = mapping.getDestination(i);
         String sDestinationName = destinationPart.getName();

         if (destinationPart.isCollection())
         {
            List list = (List)tobj.findValue(sDestinationName);

            if (list == null)
            {
               list = new ArrayList(4);
               destinationPart.setValue(tobj, list);
            }

            if (destinationPart.isPrimitive())
            {
               if (value instanceof List)
               {
                  list.addAll((List)value);
               }
               else
               {
                  list.add(value);
               }
            }
            else if (mapping.isFixedDestination(i))
            {
               if (list.isEmpty())
               {
                  tobj = destinationPart.createObject();
                  list.add(tobj);
               }
               else
               {
                  tobj = (TransferObject)list.get(0);
               }
            }
            else if (i == n && value instanceof List)
            {
               list.addAll((List)value);
            }
            else
            {
               int nLastCollection = mapping.getLastCollectionDestinationOrdinal();
               int nParentLevel = (mapping.getCollectionParent() == null) ? 0 : mapping.getCollectionParent().getLevel();
               boolean bListKey = false;

               tobj = null;
              
               while (++nList <= source.getLevel())
               {
                  TransformationSource path = source.getPath(nList);

                  if (!mapping.isFixedSource(path))
                  {
                     EndpointPart sourcePart = path.getPart();

                     if (sourcePart.isCollection())
                     {
                        if (m_listKey == null)
                        {
                           m_listKey = new ListKey();
                        }

                        if (i == nLastCollection)
                        {
                           nList = nParentLevel;
                        }

                        m_listKey.set(list, getSourceList(nList), getSourceIndex(nList), sourcePart);
                        tobj = (TransferObject)m_listMap.get(m_listKey);
                        bListKey = true;

                        break;
                     }
                     else if (i == nLastCollection && nList > nParentLevel)
                     {
                        Object sourceObject = getSourceObject(nList);

                        if (sourcePart.isPrimitive() ||
                           sourceObject == null ||
                           sourceObject == Undefined.VALUE)
                        {
                           sourceObject = getSourceObject(nList - 1);
                        }

                        if (m_listKey == null)
                        {
                           m_listKey = new ListKey();
                        }

                        m_listKey.set(list, sourceObject, 0, sourcePart);
                        tobj = (TransferObject)m_listMap.get(m_listKey);
                        bListKey = true;

                        break;
                     }
                  }
               }

               if (tobj == null)
               {
                  if (i == n)
                  {
                     tobj = (TransferObject)value;
                     list.add(tobj);
                  }
                  else
                  {
                     tobj = destinationPart.createObject();
                     list.add(tobj);
                  }

                  if (bListKey)
                  {
                     m_listMap.put(m_listKey, tobj);
                     m_listKey = null;
                  }
               }
            }
         }
         else
         {
            if (i == n)
            {
               destinationPart.setValue(tobj, value);
            }
            else
            {
               TransferObject obj = (TransferObject)tobj.findValue(sDestinationName);
              
               if (obj == null)
               {
                  obj = destinationPart.createObject();
                  destinationPart.setValue(tobj, obj);
               }
              
               tobj = obj;
            }
         }
      }
   }

   /**
    * Sets an object in the source array.
    * @param nLevel The object level.
    * @param obj The object to set.
    */
   protected void setSourceObject(int nLevel, Object obj)
   {
      m_sourceArray[SOURCE_SIZE * nLevel] = obj;
   }
  
   /**
    * Gets an object from the source array.
    * @param nLevel The object level.
    * @return The object.
    */
   protected Object getSourceObject(int nLevel)
   {
      return m_sourceArray[SOURCE_SIZE * nLevel];
   }

   /**
    * Sets a list in the source array.
    * @param nLevel The list level.
    * @param list The list to set.
    */
   protected void setSourceList(int nLevel, List list)
   {
      m_sourceArray[SOURCE_SIZE * nLevel + 1] = list;
   }

   /**
    * Gets a list from the source array.
    * @param nLevel The list level.
    * @return The list.
    */
   protected List getSourceList(int nLevel)
   {
      return (List)m_sourceArray[SOURCE_SIZE * nLevel + 1];
   }

   /**
    * Sets an index in the source array.
    * @param nLevel The index level.
    * @param nIndex The index.
    */
   protected void setSourceIndex(int nLevel, int nIndex)
   {
      m_sourceArray[SOURCE_SIZE * nLevel + 2] = Primitive.createInteger(nIndex);
   }
  
   /**
    * Gets an index from the source array.
    * @param nLevel The index level.
    * @return The index.
    */
   protected int getSourceIndex(int nLevel)
   {
      return ((Integer)m_sourceArray[SOURCE_SIZE * nLevel + 2]).intValue();
   }

   /**
    * Parses or formats the value, if the mapping has no script and is a mapping between
    * a primitive message part and a message reference.
    * @param value The value to parse/format.
    * @param source The source to parse/format.
    * @param mapping The mapping to parse/format.
    *
    * @return The parsed/formatted value.
    */
   protected Object convert(Object value, TransformationSource source, TransformationMapping mapping)
   {
      // Process only if there is no script.
      if (mapping.getOrdinal() == 0)
      {
         EndpointPart srcPart = source.getPart();
         EndpointPart dstPart = mapping.getDestination(mapping.getDestinationCount() - 1);
         Message message = null;
         boolean bParse = false;

         // Handle ref -> primitive and primitive -> ref.
         if (srcPart instanceof CompositeMessagePartRef && dstPart.isPrimitive())
         {
            message = m_context.getMetadata().getMessage(((CompositeMessagePartRef)srcPart).getRefPart().getName());
         }
         else if (dstPart instanceof CompositeMessagePartRef && srcPart.isPrimitive())
         {
            message = m_context.getMetadata().getMessage(((CompositeMessagePartRef)dstPart).getRefPart().getName());
            bParse = true;
         }

         if (message != null && message.getFormat() != null)
         {
            if (bParse)
            {
               Input in = new ObjectInput(value);
               MessageParser parser;

               if (m_parserMap == null)
               {
                  m_parserMap = new HashTab(m_context.getMetadata().getFormatCount());
               }

               parser = (MessageParser)m_parserMap.get(message.getFormat());

               if (parser == null)
               {
                  parser = (MessageParser)message.getFormat().getParser().getInstance(m_context);
                  m_parserMap.put(message.getFormat(), parser);
               }

               value = parser.parse(in, message);
            }
            else
            {
               ObjectOutput out = new ObjectOutput();
               MessageFormatter formatter;

               if (m_formatterMap == null)
               {
                  m_formatterMap = new HashTab(m_context.getMetadata().getFormatCount());
               }

               formatter = (MessageFormatter)m_formatterMap.get(message.getFormat());

               if (formatter == null)
               {
                  formatter = (MessageFormatter)message.getFormat().getFormatter().getInstance(m_context);
                  m_formatterMap.put(message.getFormat(), formatter);
               }

               formatter.format((TransferObject)value, message, out);
               value = out.getObject();
            }
         }
      }

      return value;
   }

   /**
    * Transforms a property map. Saves the current state of this Transformer instance to
    * the call stack before executing the transformation, and restores the state
    * before returning the result.
    * @param tobj The source property map.
    * @param sTransformationName The transformation name.
    * @param arguments The transformation parameters.
    * @return The transformed message.
    * @throws IntegrationException If a transformation error occurs.
    */
   public TransferObject transformNested(PropertyMap tobj, String sTransformationName, Pair arguments) throws IntegrationException
   {
      Transformation transformation = m_context.getMetadata().getTransformation(sTransformationName);
      TransferObject outputSaved = m_output;
      Object[] sourceArraySaved = m_sourceArray;
      Lookup listMapSaved = m_listMap;
      ListKey listKeySaved = m_listKey;
      Object[] scriptArraySaved = m_scriptArray;

      try
      {
         m_output = null;
         m_sourceArray = null;
         m_listMap = null;
         m_listKey = null;
         m_scriptArray = null;

         return transform(tobj, transformation, arguments);
      }
      finally
      {
         m_output = outputSaved;
         m_sourceArray = sourceArraySaved;
         m_listMap = listMapSaved;
         m_listKey = listKeySaved;
         m_scriptArray = scriptArraySaved;
      }
   }

   /**
    * Selects a derived transformation of "txf" for transforming
    * the given source end-point.
    * @param txf The base transformation to use for transforming the end-point.
    * @param endpoint The end-point to transform.
    * @return A derived transformation of "txf", or "txf" itself.
    */
   public static Transformation findDerivedTransformation(Transformation txf, TransformationEndpoint endpoint)
   {
      if (endpoint != null && txf.getDerivation() != Transformation.DERIVATION_FINAL)
      {
         Transformation result = txf.findTransformation(endpoint);

         if (result == null)
         {
            return findDerivedTransformation(txf, endpoint.getBaseEndpoint());
         }

         return (txf.isUpcast(result)) ? result : null;
      }

      if (txf.getSource().isUpcast(endpoint))
      {
         return txf;
      }

      return null;
   }

   // inner classes

   /**
    * The key for looking up a transfer object corresponding
    * to a source list index and a destination list.
    */
   protected static class ListKey
   {
      /**
       * The destination list.
       */
      protected List m_dst;
     
      /**
       * The source object.
       */
      protected Object m_src;
     
      /**
       * The source index.
       */
      protected int m_nIndex;
     
      /**
       * The end-point part.
       */
      protected EndpointPart m_part;
     
      /**
       * Sets the key data.
       * @param dst The destination list.
       * @param src The source object.
       * @param nIndex The source index.
       * @param part The source end-point part.
       */
      public void set(List dst, Object src, int nIndex, EndpointPart part)
      {
         m_dst = dst;
         m_src = src;
         m_part = part;
         m_nIndex = nIndex;
      }
     
      /**
       * @see java.lang.Object#equals(java.lang.Object)
       */
      public boolean equals(Object obj)
      {
         if (!(obj instanceof ListKey))
         {
            return false;
         }
        
         ListKey key = (ListKey)obj;
        
         return m_nIndex == key.m_nIndex && m_dst == key.m_dst &&
            m_src == key.m_src && m_part == key.m_part;
      }
     
      /**
       * @see java.lang.Object#hashCode()
       */
      public int hashCode()
      {
         return System.identityHashCode(m_dst) ^ System.identityHashCode(m_src) << 8 ^
            m_nIndex << 16 ^ System.identityHashCode(m_part);
      }
   }
}
TOP

Related Classes of nexj.core.integration.Transformer

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.