Package nexj.core.runtime.sys

Source Code of nexj.core.runtime.sys.SysReader

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

import java.sql.Timestamp;
import java.util.List;

import nexj.core.meta.Accessor;
import nexj.core.meta.Argument;
import nexj.core.meta.Attribute;
import nexj.core.meta.Event;
import nexj.core.meta.Metaclass;
import nexj.core.persistence.InvalidQueryException;
import nexj.core.runtime.ActionContext;
import nexj.core.runtime.Instance;
import nexj.core.runtime.InstanceArrayList;
import nexj.core.runtime.InstanceList;
import nexj.core.runtime.InvocationContext;
import nexj.core.runtime.InvocationContextAware;
import nexj.core.scripting.Intrinsic;
import nexj.core.scripting.Pair;
import nexj.core.scripting.Symbol;
import nexj.core.util.HashTab;
import nexj.core.util.Lookup;
import nexj.core.util.SoftHashTab;

/**
* Read assembler implementation.
*/
public class SysReader implements InvocationContextAware
{
   // constants

   /**
    * The attributes to load automatically.
    */
   private final static Object[] LOAD_ATTRIBUTES = new Object[]
   {
      Symbol.define("key"),
      Symbol.define("dispatchParent"),
      Symbol.define("dispatchAttribute"),
      Symbol.define("dispatchValues"),
      Symbol.define("dispatchGroup"),
      Symbol.define("dispatched"),
      Symbol.define("offset"),
      Symbol.define("count"),
      Symbol.define("xlock"),
      Symbol.define("bookmark"),
      Symbol.define("next"),
      Symbol.define("inclusive"),
      Symbol.define("parents"),
      Symbol.define("results"),
      Symbol.define("where"),
      Symbol.define("orderBy"),
      Symbol.define("event")
   };

   // associations

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

   // operations

   /**
    * @see nexj.core.runtime.InvocationContextAware#setInvocationContext(nexj.core.runtime.InvocationContext)
    */
   public void setInvocationContext(InvocationContext context)
   {
      m_context = context;
   }

   /**
    * Collects the instances specified by a given association path in a list.
    * @param assoc The association path.
    * @param instance The instance relative to which the association is.
    * @param results The output list.
    */
   protected static void collectAssociatedInstances(Pair assoc, Instance instance, InstanceList results)
   {
      if (assoc == null)
      {
         results.add(instance, InstanceList.REPLACE | InstanceList.DIRECT);
      }
      else
      {
         Object value = instance.getValue(((Symbol)assoc.getHead()).getName());

         if (value != null)
         {
            if (value instanceof InstanceList)
            {
               assoc = assoc.getNext();

               InstanceList list = (InstanceList)value;

               for (int i = 0, nCount = list.size(); i < nCount; ++i)
               {
                  collectAssociatedInstances(assoc, list.getInstance(i), results);
               }
            }
            else if (value instanceof Instance)
            {
               collectAssociatedInstances(assoc.getNext(), (Instance)value, results);
            }
            else
            {
               throw new InvalidQueryException("err.persistence.primitiveAssocQuery",
                  new Object[]{assoc.getHead(), instance.getMetaclass().getName()});
            }
         }
      }
   }

   /**
    * Checks if the dispatch conditions are satisfied.
    * @param reader The reader object.
    * @return True if the query should be executed.
    */
   protected boolean isEnabled(Accessor reader)
   {
      Accessor dispGroup = (Accessor)reader.getValue("dispatchGroup");

      if (dispGroup != null)
      {
         boolean bDispatched = ((Boolean)dispGroup.getValue("dispatched")).booleanValue();

         if (dispGroup == reader)
         {
            if (bDispatched)
            {
               return false;
            }
         }
         else if (((Boolean)reader.getValue("dispatched")).booleanValue() != bDispatched)
         {
            return false;
         }
      }

      Accessor dispParent = (Accessor)reader.getValue("dispatchParent");

      if (dispParent == null)
      {
         if (dispGroup != null && reader != dispGroup)
         {
            reader.setValue("dispatched", Boolean.TRUE);
         }

         return true;
      }

      InstanceList resultList = (InstanceList)dispParent.getValue("results");

      if (resultList == null || resultList.isEmpty())
      {
         return false;
      }

      dispParent = resultList.getInstance(0);

      Pair dispAssoc = (Pair)reader.getValue("dispatchAttribute");
      Pair dispValues = (Pair)reader.getValue("dispatchValues");

      if (dispAssoc == null)
      {
         Metaclass metaclass = dispParent.getMetaclass();

         for (; dispValues != null; dispValues = dispValues.getNext())
         {
            if (m_context.getMetadata().getMetaclass(((Symbol)dispValues.getHead()).getName()).isUpcast(metaclass))
            {
               if (dispGroup != null)
               {
                  dispGroup.setValue("dispatched", Boolean.TRUE);

                  if (reader != dispGroup)
                  {
                     reader.setValue("dispatched", Boolean.TRUE);
                  }
               }

               return true;
            }
         }

         return false;
      }

      Object value;

      for (value = dispParent; value != null && dispAssoc != null; dispAssoc = dispAssoc.getNext())
      {
         dispParent = (Accessor)value;

         Attribute attribute = dispParent.getMetaclass().getAttribute(((Symbol)dispAssoc.getHead()).getName());

         attribute.checkReadAccess(m_context.getPrivilegeSet());
         value = dispParent.getValue(attribute.getOrdinal());
      }

      for (; dispValues != null; dispValues = dispValues.getNext())
      {
         if (Intrinsic.equal(value, dispValues.getHead()))
         {
            if (dispGroup != null)
            {
               dispGroup.setValue("dispatched", Boolean.TRUE);

               if (reader != dispGroup)
               {
                  reader.setValue("dispatched", Boolean.TRUE);
               }
            }

            return true;
         }
      }

      return false;
   }

   /**
    * Sets an empty result list on the reader.
    * @param reader The reader.
    * @return The result list.
    */
   protected static InstanceList setEmptyResult(Accessor reader)
   {
      reader.setValue("results", null);

      return null;
   }

   /**
    * Generates a lookup key to be used given a key value as a string
    * @param context Current InvocationContext
    * @param sKey String containing the key to use
    * @return A lookup key to be used when looking for cached results
    */
   public static Pair getLookupKey(InvocationContext context, String sKey)
   {
      return Pair.list("read", (context.getUser() != null) ? context.getUser().getOID() : null, sKey);
   }

   /**
    * Checks if the given key and session have expired and a query should be performed.
    * @param context InvocationContext
    * @param key Pair indicating the key to check
    * @param session Session identifier
    * @return
    */
   public static boolean isKeyExpired(InvocationContext context, Pair key, Object session)
   {
      Lookup sessionMap = (Lookup)context.getGlobalCache().get(key);

      if (sessionMap != null)
      {
         Timestamp tsExpiration = (Timestamp)sessionMap.get(session);

         if (tsExpiration != null && tsExpiration.getTime() > context.getUnitOfWork().getTime())
         {
            return false;
         }
      }

      return true;
   }

   /**
    * Finds the value of a named argument in a list.
    * @param event The event object.
    * @param sName The argument name.
    * @param args The event argument value list.
    * @return The argument value, or null if not found.
    */
   public static Object findValue(Event event, String sName, Pair args)
   {
      Argument arg = event.findArgument(sName);

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

      for (int i = arg.getOrdinal(); i != 0; --i)
      {
         args = args.getNext();
      }

      return args.getHead();
   }

   /**
    * Sets the value of a named argument in an array.
    * @param event The event object.
    * @param sName The argument name.
    * @param args The event argument value array.
    * @param The value to set.
    */
   public static void setValue(Event event, String sName, Object[] args, Object value)
   {
      Argument arg = event.findArgument(sName);

      if (arg != null)
      {
         args[arg.getOrdinal() + 1] = value;
      }
   }

   /**
    * Updates the attributes if a custom event is specified.
    * @param reader The command object containing the query.
    */
   public void updateAttributes(Accessor reader, ActionContext actx)
   {
      Object value = reader.getValue("event");

      if (value != null)
      {
         Pair args;

         if (value instanceof Pair)
         {
            args = (Pair)value;
         }
         else
         {
            args = Pair.list(Symbol.define((String)value), reader.getValue("attributes"),
               reader.getValue("where"), reader.getValue("orderBy"), reader.getValue("count"),
               reader.getValue("offset"), reader.getValue("xlock"));

            reader.setValue("event", args);
         }

         Symbol symbol = (Symbol)args.getHead();

         args = args.getNext();

         Event event = (Event)m_context.getMetadata()
            .getMetaclass(((Symbol)reader.getValue("class")).getName())
            .getSelector(symbol).getMember(Pair.length(args));

         reader.setValue("attributes", findValue(event, "attributes", args));
         reader.setValue("where", findValue(event, "where", args));
         reader.setValue("orderBy", findValue(event, "orderBy", args));
         reader.setValue("count", findValue(event, "count", args));
         reader.setValue("offset", findValue(event, "offset", args));
         reader.setValue("xlock", findValue(event, "xlock", args));
      }
   }

   /**
    * Reads the instances specified in the reader command object query.
    * @param reader The command object containing the query.
    */
   public InstanceList read(Accessor reader, ActionContext actx)
   {
      boolean bUOWGlobalSaved = m_context.isUnitOfWorkGlobal();

      try
      {
         m_context.setUnitOfWorkGlobal(false);

         reader.invoke("load", LOAD_ATTRIBUTES);

         // Check if the data has changed
         Pair key = null;
         String sKey = (String)reader.getValue("key");
         Lookup sessionMap = null;

         if (sKey != null)
         {
            key = getLookupKey(m_context, sKey);

            if (!isKeyExpired(m_context, key, reader.getValue("session")))
            {
               return setEmptyResult(reader);
            }
         }

         if (!isEnabled(reader))
         {
            return setEmptyResult(reader);
         }

         Symbol classSym = (Symbol)reader.getValue("class");
         Pair attributes = (Pair)reader.getValue("attributes");
         Object where = reader.getValue("where");
         Pair orderBy = (Pair)reader.getValue("orderBy");
         Number count = (Number)reader.getValue("count");
         Number offset = (Number)reader.getValue("offset");
         Boolean xlock = (Boolean)reader.getValue("xlock");
         Pair bookmark = (Pair)reader.getValue("bookmark");
         Boolean next = (Boolean)reader.getValue("next");
         Boolean inclusive = (Boolean)reader.getValue("inclusive");
         List parentList = (List)reader.getValue("parents");
         Object args = reader.getValue("event");
         Metaclass metaclass = m_context.getMetadata().getMetaclass(classSym.getName());
         Object[] argArray = null;
         Event event = null;

         if (args instanceof Pair)
         {
            argArray = Pair.toArray((Pair)args);
            event = (Event)metaclass.getSelector((Symbol)argArray[0]).getMember(argArray.length - 1);
            argArray[0] = metaclass;

            Argument result = event.getResult();

            if (result != null && !result.getType().isPrimitive())
            {
               metaclass = (Metaclass)result.getType();
            }
         }
         else if (args instanceof String)
         {
            event = (Event)metaclass.getSelector((String)args).getMember(6);
            argArray = new Object[]{metaclass, attributes, where, orderBy, count, offset, xlock};
         }

         metaclass.checkReadAccess(m_context.getPrivilegeSet());

         Pair security = metaclass.checkReadAccess(attributes, m_context.getPrivilegeSet());

         metaclass.checkExpressionAccess(where,  m_context.getPrivilegeSet());

         if (parentList != null)
         {
            List assocList = (List)reader.getValue("associations");
            boolean bConj = false;

            for (int i = parentList.size() - 1; i >= 0; --i)
            {
               Accessor parent = (Accessor)parentList.get(i);
               InstanceList resultList = (InstanceList)parent.getValue("results");

               if (resultList == null || resultList.size() == 0)
               {
                  return setEmptyResult(reader);
               }

               Object assoc = assocList.get(i);

               if (assoc !=  null && !(assoc instanceof Pair))
               {
                  assoc = new Pair(assoc);
               }

               Instance parentInstance = resultList.getInstance(0);
               Metaclass parentMetaclass = parentInstance.getMetaclass();

               // Check for attributes without persistence mappings
               if (i == 0)
               {
                  Metaclass containerMetaclass = parentMetaclass;

                  for (Pair pair = (Pair)assoc; pair != null; pair = pair.getNext())
                  {
                     Attribute attribute = containerMetaclass.findAttribute((Symbol)pair.getHead());

                     if (attribute == null)
                     {
                        break;
                     }

                     attribute.checkReadAccess(m_context.getPrivilegeSet());

                     if (!attribute.isPersistent() &&
                        !attribute.getType().isPrimitive() &&
                        event == null &&
                        ((Metaclass)attribute.getType()).findEvent("read", 6)
                           .findAction("main").getDeclarator().getBase() == null)
                     {
                        if (parentList.size() > 1)
                        {
                           throw new InvalidQueryException("err.persistence.multipleParentsWithoutPersistence",
                              new Object[]{classSym, assoc, parentMetaclass.getName()});
                        }

                        if (where != null || bookmark != null || orderBy != null)
                        {
                           throw new InvalidQueryException("err.persistence.whereWithoutPersistence",
                              new Object[]{classSym, assoc, parentMetaclass.getName()});
                        }

                        resultList = new InstanceArrayList();
                        collectAssociatedInstances((Pair)assoc, parentInstance, resultList);

                        int nOffset = (offset == null) ? 0 : offset.intValue();
                        int nCount = (count == null) ? -1 : count.intValue();

                        if (nOffset < 0)
                        {
                           nOffset = 0;
                        }

                        if (nCount < 0 || nCount > resultList.size() - nOffset)
                        {
                           nCount = resultList.size() - nOffset;

                           if (nCount < 0)
                           {
                              nCount = 0;
                           }
                        }

                        if (nOffset > 0 || resultList.size() > nCount)
                        {
                           InstanceList list = new InstanceArrayList(nCount);

                           for (int k = 0; k < nCount; ++k)
                           {
                              list.add(resultList.getInstance(k + nOffset), InstanceList.REPLACE | InstanceList.DIRECT);
                           }

                           resultList = list;
                        }

                        if (next != null && !next.booleanValue())
                        {
                           resultList.reverse();
                        }

                        for (int k = 0; k < nCount; ++k)
                        {
                           resultList.getInstance(k).invoke("load", attributes);
                        }

                        reader.setValue("results", resultList);

                        return resultList;
                     }

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

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

               assoc = new Pair(Symbol.ATAT, new Pair(parentMetaclass.getSymbol(), assoc));

               Pair cond = Pair.binary(Symbol.EQ, assoc, parentInstance.getOID());

               if (bConj)
               {
                  where = new Pair(cond, where);
               }
               else if (where != null)
               {
                  where = new Pair(cond, new Pair(where));
                  bConj = true;
               }
               else
               {
                  where = cond;
               }
            }

            if (bConj)
            {
               where = new Pair(Symbol.AND, where);
            }
         }

         // Handle the bookmark
         boolean bNext = true;

         if (next != null && orderBy != null)
         {
            bNext = next.booleanValue();

            if (!bNext)
            {
               // Flip sort order
               Pair pair = orderBy;
               Pair last = orderBy = null;

               for (; pair != null; pair = pair.getNext())
               {
                  Pair head = (Pair)pair.getHead();
                  Pair item = new Pair(new Pair(head.getHead(),
                     Boolean.valueOf(!((Boolean)head.getTail()).booleanValue())));

                  if (last == null)
                  {
                     orderBy = item;
                  }
                  else
                  {
                     last.setTail(item);
                  }

                  last = item;
               }
            }

            // The bookmark has the following format: (value1 ... valueN)
            // If values are missing, then the start or the end of the recordset is
            // retrieved, depending on the value of next (inclusive is ignored in this case)

            if (bookmark != null)
            {
               where = addBookmarkToWhere(where, orderBy, bookmark, inclusive);
            }
         }

         // This takes care of the bookmark attributes access rights
         metaclass.checkOrderByAccess(orderBy, m_context.getPrivilegeSet());
         attributes = Pair.nconc(security, attributes);

         InstanceList resultList;
        
         if (event != null)
         {
            setValue(event, "attributes", argArray, attributes);
            setValue(event, "where", argArray, where);
            setValue(event, "orderBy", argArray, orderBy);

            resultList = (InstanceList)event.invoke(argArray, m_context.getMachine());
         }
         else
         {
            resultList = (InstanceList)metaclass.invoke("read",
               new Object[]{attributes, where, orderBy, count, offset, xlock});
         }

         if (!bNext)
         {
            resultList.reverse();
         }

         reader.setValue("results", resultList);

         if (key != null)
         {
            Lookup cacheMap = null;

            if (sessionMap == null)
            {
               sessionMap = new SoftHashTab(1);
               cacheMap = new HashTab(1);
            }

            synchronized (sessionMap)
            {
               sessionMap.put(reader.getValue("session"),
                  new Timestamp(m_context.getUnitOfWork().getTime() +
                     ((Number)reader.getValue("expiration")).longValue()));
            }

            if (cacheMap != null)
            {
               cacheMap.put(key, sessionMap);
               m_context.getGlobalCache().update(null, cacheMap, m_context.getUnitOfWork().getTime());
            }
         }

         return resultList;
      }
      finally
      {
         m_context.setUnitOfWorkGlobal(bUOWGlobalSaved);
      }
   }

   /**
    * Add bookmark to where.
    * @param where Where.
    * @param orderBy Order by.
    * @param bookmark Bookmark: (value1 ... valueN).
    * @param inclusive Whether inclusive.
    * @return New where containing bookmark condition.
    */
   public static Object addBookmarkToWhere(Object where, Pair orderBy, Pair bookmark, Boolean inclusive)
   {
      // Add conditions to the where clause
      boolean bInclusive = inclusive.booleanValue();
      Pair lastOrderBy = null;
      boolean bDisj = false;
      Pair disj = null;

      do
      {
         boolean bConj = false;
         Pair conj = null;
         Pair lastConj = null;

         for (Pair pair = orderBy, mark = bookmark;;
            pair = pair.getNext(), mark = mark.getNext())
         {
            Symbol op;

            if (pair.getTail() == lastOrderBy || mark.getTail() == null)
            {
               lastOrderBy = pair;

               if (!((Boolean)((Pair)pair.getHead()).getTail()).booleanValue())
               {
                  op = (bInclusive && mark.getTail() == null) ? Symbol.LE : Symbol.LT;
               }
               else
               {
                  op = (bInclusive && mark.getTail() == null) ? Symbol.GE : Symbol.GT;
               }
            }
            else
            {
               op = Symbol.EQ;
            }

            Pair cmp = Pair.binary(op, ((Pair)pair.getHead()).getHead(), mark.getHead());

            if (conj == null)
            {
               conj = lastConj = cmp;
            }
            else if (bConj)
            {
               Pair newConj = new Pair(cmp);

               lastConj.setTail(newConj);
               lastConj = newConj;
            }
            else
            {
               lastConj = new Pair(cmp);
               conj = new Pair(conj, lastConj);
               bConj = true;
            }

            if (pair == lastOrderBy)
            {
               if (bConj)
               {
                  conj = new Pair(Symbol.AND, conj);
               }

               break;
            }
         }

         if (disj == null)
         {
            disj = conj;
         }
         else if (bDisj)
         {
            disj = new Pair(conj, disj);
         }
         else
         {
            disj = new Pair(conj, new Pair(disj));
            bDisj = true;
         }
      }
      while (lastOrderBy != orderBy);

      if (bDisj)
      {
         disj = new Pair(Symbol.OR, disj);
      }

      where = Pair.commutative(Symbol.AND, where, disj);

      if (bookmark.getTail() != null)
      {
         where = Pair.commutative(Symbol.AND, where,
            Pair.binary((!((Boolean)((Pair)orderBy.getHead()).getTail()).booleanValue())
               ? Symbol.LE : Symbol.GE, ((Pair)orderBy.getHead()).getHead(), bookmark.getHead()));
      }

      return where;
   }
}
TOP

Related Classes of nexj.core.runtime.sys.SysReader

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.