db-ojb-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From arm...@apache.org
Subject svn commit: r520721 - in /db/ojb/trunk/src/java/org/apache/ojb/broker: accesslayer/sql/SqlQueryStatement.java accesslayer/sql/TableAliasHandler.java metadata/ClassDescriptor.java util/AttributeTokenizer.java
Date Wed, 21 Mar 2007 02:28:30 GMT
Author: arminw
Date: Tue Mar 20 19:28:29 2007
New Revision: 520721

URL: http://svn.apache.org/viewvc?view=rev&rev=520721
Log:
fix for OJB-133

Added:
    db/ojb/trunk/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java
Modified:
    db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java
    db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/TableAliasHandler.java
    db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java

Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java?view=diff&rev=520721&r1=520720&r2=520721
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java Tue
Mar 20 19:28:29 2007
@@ -20,7 +20,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
 
 import org.apache.ojb.broker.PersistenceBrokerException;
 import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
@@ -46,7 +45,7 @@
 import org.apache.ojb.broker.query.SelectionCriteria;
 import org.apache.ojb.broker.query.SqlCriteria;
 import org.apache.ojb.broker.query.UserAlias;
-import org.apache.ojb.broker.util.SqlHelper;
+import org.apache.ojb.broker.util.AttributeTokenizer;
 import org.apache.ojb.broker.util.logging.Logger;
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 
@@ -91,6 +90,12 @@
     {
         super(aPlatform, aLogger);
 
+        if(!aCld.isMappedToTable() && !aQuery.getWithExtents())
+        {
+            throw new PersistenceBrokerException("Can't query objects of abstract class/interface
'"
+                    + aCld.getClassNameOfObject() + "' with disabled 'extents' - Query.setWithExtents(false)");
+        }
+
         if (aLogger ==null)
         {
             setLogger(LoggerFactory.getLogger(SqlQueryStatement.class));
@@ -153,13 +158,12 @@
 
         if (result == null)
         {
-            StringTokenizer st = SqlHelper.tokenizeAttribute(attr);
+            AttributeTokenizer attrTok = new AttributeTokenizer(attr);
             result = new AttributeInfo(attr);
-
-            while (st.hasMoreTokens())
+            while (attrTok.hasNext())
             {
-                String token = st.nextToken();
-                if (SqlHelper.isAttribute(token))
+                String token = attrTok.next();
+                if (attrTok.currentIsAttribute())
                 {
                     result.add(getSingleAttributeInfo(token, useOuterJoins, aUserAlias, pathClasses));
                 }
@@ -168,14 +172,12 @@
                     result.add(token);
                 }
             }
-
             m_attributeCache.put(cacheKey, result);
         }
         else
         {
             result.reset();    // Reset extent index
         }
-
         return result;
     }
 
@@ -371,18 +373,27 @@
     {
         FieldDescriptor fld = null;
 
-        // Search Join Structure for attribute
-        // TODO BRJ: imo we have to consider 'superClass' joins only
+        /*
+            Search Join Structure for attribute
+            BRJ: imo we have to consider 'superClass' joins only
+            arminw: If a report query with aggreate function is used, it can be
+            necessary to resolve a join:
+            ReportQueryByCriteria q = QueryFactory.newReportQuery(BookReview.class, crit);
+            q.setAttributes(new String[]{"name", "sum(author.books)"});
+            in this case we need to resolve the path 1:1 to Author and 1:n to Book list.
+            Seems that OJB-94 doesn't appear again after changing these lines.
+        */
         if (aTableAlias.joins != null)
         {
             Iterator itr = aTableAlias.joins.iterator();
             while (itr.hasNext())
             {
                 Join join = (Join) itr.next();
-                if ("superClass".equals(join.name))
-                {
-                    fld = getFieldDescriptor(join.right, attrName);
-                }
+//                if ("superClass".equals(join.name))
+//                {
+//                    fld = getFieldDescriptor(join.right, attrName);
+//                }
+                fld = getFieldDescriptor(join.right, attrName);
                 if (fld != null)
                 {
                     break;

Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/TableAliasHandler.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/TableAliasHandler.java?view=diff&rev=520721&r1=520720&r2=520721
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/TableAliasHandler.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/accesslayer/sql/TableAliasHandler.java Tue
Mar 20 19:28:29 2007
@@ -39,7 +39,7 @@
 import org.apache.ojb.broker.query.SelectionCriteria;
 import org.apache.ojb.broker.query.SqlCriteria;
 import org.apache.ojb.broker.query.UserAlias;
-import org.apache.ojb.broker.util.SqlHelper;
+import org.apache.ojb.broker.util.AttributeTokenizer;
 import org.apache.ojb.broker.util.logging.Logger;
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 
@@ -85,7 +85,7 @@
     /**
      * Test if it's the special M_N_Alias.
      * @param aTableAlias
-     * @return
+     * @return true if m:n alias
      */
     static boolean isMNAlias(TableAlias aTableAlias)
     {
@@ -284,7 +284,7 @@
      * Set the TableAlias for aPath
      * @param aPath
      * @param hintClasses 
-     * @param TableAlias
+     * @param anAlias
      */
     private void setTableAliasForPath(String aPath, List hintClasses, TableAlias anAlias)
     {
@@ -773,7 +773,7 @@
 	 */
 	private void buildJoinTreeForAttribute(String anAttribName, boolean useOuterJoin, UserAlias
aUserAlias, Map pathClasses)
 	{
-		String pathName = SqlHelper.cleanPath(anAttribName);
+		String pathName = new AttributeTokenizer(anAttribName).getCleanPath();
 		int sepPos = pathName.lastIndexOf(".");
 
 		if (sepPos >= 0)
@@ -904,7 +904,7 @@
                     String extTable = extCld.getFullTableName();
 
                     // BRJ : Use the first non abstract extent if the main cld is abstract
-                    if (aCld.isAbstract() && i == firstNonAbstractExtentIndex)
+                    if (!aCld.isMappedToTable() && i == firstNonAbstractExtentIndex)
                     {
                         this.cld = extCld;
                         this.table = extTable;

Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java?view=diff&rev=520721&r1=520720&r2=520721
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java Tue Mar 20 19:28:29
2007
@@ -17,7 +17,6 @@
 
 import java.io.Serializable;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -33,11 +32,12 @@
 import org.apache.commons.collections.set.ListOrderedSet;
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.SystemUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.lang.builder.ToStringStyle;
 import org.apache.ojb.broker.locking.IsolationLevels;
+import org.apache.ojb.broker.util.AttributeTokenizer;
 import org.apache.ojb.broker.util.ClassHelper;
-import org.apache.ojb.broker.util.SqlHelper;
 import org.apache.ojb.broker.util.XmlHelper;
 import org.apache.ojb.broker.util.logging.LoggerFactory;
 
@@ -137,10 +137,7 @@
      * the described class
      */
     private Class m_Class = null;
-    /**
-     * whether the described class is abstract
-     */
-    private boolean isAbstract = false;
+
     /**
      * the table name used to store the scalar attributes of this class
      */
@@ -325,7 +322,6 @@
     public void setClassOfObject(Class c)
     {
         m_Class = c;
-        isAbstract = Modifier.isAbstract(m_Class.getModifiers());
         // TODO : Shouldn't the HashMap in DescriptorRepository be updated as well?
     }
 
@@ -774,10 +770,11 @@
     }
 
     /**
-     * return the FieldDescriptor for the Attribute referenced in the path<br>
-     * the path may contain simple attribut names, functions and path expressions
-     * using relationships <br>
+     * Return the {@link FieldDescriptor} for the attribute referenced in the path.
+     * The path may contain simple attribut names, functions and path expressions
+     * using relationships <br/>
      * ie: name, avg(price), adress.street
+     *
      * @param aPath the path to the attribute
      * @param pathHints a Map containing the class to be used for a segment or <em>null</em>
      * if no segment was used.
@@ -801,19 +798,6 @@
     }
 
     /**
-     * return the FieldDescriptor for the Attribute referenced in the path<br>
-     * the path may contain simple attribut names, functions and path expressions
-     * using relationships <br>
-     * ie: name, avg(price), adress.street
-     * @param aPath the path to the attribute
-     * @return the FieldDescriptor or null (ie: for m:n queries)
-     */
-    public FieldDescriptor getFieldDescriptorForPath(String aPath)
-    {
-        return getFieldDescriptorForPath(aPath, null);
-    }
-
-    /**
      * Returns the first found autoincrement field
      * defined in this class descriptor. Use carefully
      * when multiple autoincrement field were defined.
@@ -913,7 +897,7 @@
             {
                 FieldDescriptor[] fields;
                 // 1.b if not an interface The classdescriptor must have FieldDescriptors
-                fields = getFieldDescriptions();
+                fields = getFieldDescriptor(false);
                 // now collect all PK fields
                 for (int i = 0; i < fields.length; i++)
                 {
@@ -1081,12 +1065,13 @@
     }
 
     /**
-     * return all AttributeDescriptors for the path<br>
+     * Return all AttributeDescriptors for the path<br>
      * ie: partner.addresses.street returns a Collection of 3 AttributeDescriptors
      * (ObjectReferenceDescriptor, CollectionDescriptor, FieldDescriptor)<br>
      * ie: partner.addresses returns a Collection of 2 AttributeDescriptors
      * (ObjectReferenceDescriptor, CollectionDescriptor)
-     * @param aPath the cleaned path to the attribute
+     *
+     * @param aPath the path to the attribute
      * @return ArrayList of AttributeDescriptors
      */
     public ArrayList getAttributeDescriptorsForPath(String aPath)
@@ -1100,14 +1085,15 @@
      * (ObjectReferenceDescriptor, CollectionDescriptor, FieldDescriptor)<br>
      * ie: partner.addresses returns a Collection of 2 AttributeDescriptors
      * (ObjectReferenceDescriptor, CollectionDescriptor)
-     * @param aPath the cleaned path to the attribute
+     *
+     * @param aPath the path to the attribute
      * @param pathHints a Map containing the class to be used for a segment or <em>null</em>
      * if no segment was used.
      * @return ArrayList of AttributeDescriptors
      */
     public ArrayList getAttributeDescriptorsForPath(String aPath, Map pathHints)
     {
-        return getAttributeDescriptorsForCleanPath(SqlHelper.cleanPath(aPath), pathHints);
+        return getAttributeDescriptorsForCleanPath(new AttributeTokenizer(aPath).getCleanPath(),
pathHints);
     }
 
     /**
@@ -1116,7 +1102,8 @@
      * (ObjectReferenceDescriptor, CollectionDescriptor, FieldDescriptor)<br>
      * ie: partner.addresses returns a Collection of 2 AttributeDescriptors
      * (ObjectReferenceDescriptor, CollectionDescriptor)
-     * @param aPath the cleaned path to the attribute
+     *
+     * @param aPath the "cleaned path" (without function expressions) to the attribute
      * @param pathHints a Map containing the class to be used for a segment or <em>null</em>
      * if no segment is used.
      * @return ArrayList of AttributeDescriptors
@@ -1193,17 +1180,6 @@
     }
 
     /**
-     * Return true, if the described class is
-     * an 'interface'. That is if the class is <b>not</b> mapped to a table.
-     * @deprecated use ! isMappedToTable()
-     * @see #isMappedToTable()
-     */
-    public boolean isInterface()
-    {
-        return !isMappedToTable();
-    }
-
-    /**
      * Return true if the class is mapped to a table.
      */
     public boolean isMappedToTable()
@@ -1212,14 +1188,6 @@
     }
 
     /**
-     * @return boolean true if the mapped class is abstract
-     */
-    public boolean isAbstract()
-    {
-        return isAbstract;
-    }
-
-    /**
      * Returns acceptLocks.
      * @return boolean
      */
@@ -1314,7 +1282,7 @@
      */
     public void setTableName(String str)
     {
-        m_TableName = str;
+        if(!StringUtils.isEmpty(str)) m_TableName = str;
     }
 
     /**
@@ -1343,7 +1311,7 @@
      */
     public void setSchema(String schema)
     {
-        this.schema = schema;
+        if(!StringUtils.isEmpty(schema)) this.schema = schema;
     }
 
     /**

Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java?view=auto&rev=520721
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java Tue Mar 20 19:28:29
2007
@@ -0,0 +1,332 @@
+package org.apache.ojb.broker.util;
+
+/* Copyright 2002-2007 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Map;
+
+import org.apache.commons.collections.map.LRUMap;
+
+/**
+ * This class help:
+ * <ul>
+ *     <li>
+ * to split query-attribute strings into token
+ * </li>
+ *     <li>
+ * to identify the type of the next or current token - an attribute, a function or delimiter.
+ * <br/>
+ * Attribute string: 'abs(sum(author.book.pages))' will be split to
+ * <br/>
+ * 'abs'-->noneAttr, '('-->noneAttr, 'sum'-->nonAttr, 'author.book.pages'-->Attr
...
+ * </li>
+ *     <li>
+ * to delete functions and keywords from the query-attribute to get the "clean path" of the
+ * attribute string - see {@link #getCleanPath()}
+ * </li>
+ * </ul>
+ *
+ * @version $Id$
+ */
+public final class AttributeTokenizer
+{
+    /**
+     * Delimiters used to tokenize attributes.
+     */
+    private static final String DELIMITERS = "( ),+-/*";
+    private static final String STR_OPEN_BRAKE = "(";
+    private static final String STR_AS = "as";
+    private static final String STR_BLANK = " ";
+    private static final String STR_DISTINCT = "distinct";
+
+    private static final Map tokenListsMap = new LRUMap(500);
+
+    private final String attribute;
+    private final List tokenList;
+    private final int tokenListSize;
+    private int index;
+    private String cleanPath;
+
+    public AttributeTokenizer(String attribute)
+    {
+        this.attribute = attribute;
+        this.tokenList = getTokenList();
+        this.tokenListSize = this.tokenList.size();
+        reset();
+    }
+
+    /**
+     * Lookup the token list of the associated attribute string
+     * (Internal a LRUMap is used to cache the token lists of attributes)
+     *
+     * @return The token list of the attribute.
+     */
+    private List getTokenList()
+    {
+        List result = (List) tokenListsMap.get(attribute);
+        if(result == null)
+        {
+            result = new ArrayList();
+            StringTokenizer st = new StringTokenizer(attribute, DELIMITERS, true);
+            while(st.hasMoreTokens())
+            {
+                result.add(st.nextToken());
+            }
+            tokenListsMap.put(attribute, result);
+        }
+        return result;
+    }
+
+    /**
+     * Reset this class to initial state.
+     */
+    public void reset()
+    {
+        index = -1;
+    }
+
+    /**
+     * Return the current token string. Don't call this in initial state,
+     * at least the first {@link #next()} call has to be done.
+     *
+     * @return Return the current token string.
+     */
+    public String current()
+    {
+        return getToken(index);
+    }
+
+    /**
+     * Tests if there are more tokens available.
+     * If this method returns <tt>true</tt>, then a subsequent call to
+     * {@link #next() <tt>nextToken</tt>} will successfully
+     * return a token.
+     *
+     * @return  <code>true</code> if and only if there is at least one token
string
+     *          in the attribute after the current position; <code>false</code>
+     *          otherwise.
+     */
+    public boolean hasNext()
+    {
+        return tokenListSize > 0 && index < tokenListSize - 1;
+    }
+
+    /**
+     * Returns the next token from the attribute string.
+     *
+     * @return The next token from the attribute string.
+     */
+    public String next()
+    {
+        skip();
+        return current();
+    }
+
+    /**
+     * Skip the next token from the attribute string.
+     */
+    public void skip()
+    {
+        ++index;
+    }
+
+    /**
+     * Answer <em>true</em> if the next token string is a query-attribute.
+     *
+     * @return The result of the attribute check.
+     */
+    public boolean nextIsAttribute()
+    {
+        boolean result = hasNext();
+        // expect all single word expressions are attributes
+        if(result && tokenListSize > 1)
+        {
+            int nextIndex = index + 1;
+            result = !isDelimiter(nextIndex) && !isFunction(nextIndex) &&
!isKeyword(nextIndex);
+        }
+        return result;
+    }
+
+    /**
+     * Answer <em>true</em> if the current token string is a query-attribute.
+     *
+     * @return The result of the attribute check.
+     */
+    public boolean currentIsAttribute()
+    {
+        boolean result = index > -1;
+        // expect all single word expressions are attributes
+        if(result && tokenListSize > 1)
+        {
+            result = !isDelimiter(index) && !isFunction(index) && !isKeyword(index);
+        }
+        return result;
+    }
+
+    /**
+     * Remove functions/keywords, '(' and ')' from path.
+     * <br/>
+     * <pre>
+     * attribute-str: 'id' --> 'id'
+     * attribute-str: 'sum(id)' --> 'id'
+     * attribute-str: 'sum ( id) ' --> 'id'
+     * attribute-str: '  sum ( id  ) ' --> 'id'
+     * attribute-str: 'abs(sum(id))' --> 'id'
+     * attribute-str: 'abs (sum(id  ))' --> 'id'
+     * attribute-str: 'count(distinct author.books.pages)' --> 'author.books.pages'
+     * </pre>
+     *
+     * @return The "clean path" of the attribute string.
+     */
+    public String getCleanPath()
+    {
+        if(cleanPath == null)
+        {
+            int oldIndex = index;
+
+            try
+            {
+                reset();
+                String result = attribute;
+                while (hasNext())
+                {
+                    if (nextIsAttribute())
+                    {
+                        result = next();
+                        break;
+                    }
+                    else
+                    {
+                        // skip
+                        skip();
+                    }
+                }
+                cleanPath = result;
+            }
+            finally
+            {
+                index = oldIndex;
+            }
+        }
+        return cleanPath;
+    }
+
+    private boolean isFunction(int index)
+    {
+        String current = getToken(index);
+        return STR_OPEN_BRAKE.equals(getNextNonBlankToken(index))
+                && !(STR_OPEN_BRAKE.equals(current) || STR_BLANK.equals(current));
+    }
+
+    private boolean isKeyword(int index)
+    {
+        String token = getToken(index);
+        return (STR_AS.equals(token) || STR_DISTINCT.equals(token)) && !isDelimiter(getNextNonBlankToken(index));
+    }
+
+    private boolean isDelimiter(int index)
+    {
+        return isDelimiter(getToken(index));
+    }
+
+    private boolean isDelimiter(String str)
+    {
+        return str != null && DELIMITERS.indexOf(str) > -1;
+    }
+
+    private boolean isBlank(String str)
+    {
+        return str != null && STR_BLANK.equals(str);
+    }
+
+    private String getToken(int index)
+    {
+        return index < tokenListSize ? (String) tokenList.get(index) : null;
+    }
+
+    private String getNextNonBlankToken(int index)
+    {
+        String result = null;
+        for(int i = index + 1; i < tokenListSize; i++)
+        {
+            String str = (String) tokenList.get(i);
+            if(!isBlank(str))
+            {
+                result = str;
+                break;
+            }
+        }
+        return result;
+    }
+
+// arminw:
+// only useful for development!!
+//
+//    public static void main(String[] args)
+//    {
+//        printClean("id");
+//        printClean("sum(id)");
+//        printClean("sum ( id) ");
+//        printClean("  sum ( id  ) ");
+//        printClean("abs(sum(id))");
+//        printClean("abs (sum(id  ))");
+//        printClean("count(distinct id)");
+//        printClean("  count   (   distinct   id  )  ");
+//        printClean("  count   (   distinct   distinct  )  ");
+//        printClean("  count   (   distinct   as  )  ");
+//        printClean("((  count   (   distinct   as  )  ))");
+//        printClean("sum(curdate.sum)");
+//        printClean("abs(as)");
+//
+//        System.out.println("------------------------");
+//        printAttr("id");
+//        printAttr("sum(id)");
+//        printAttr("sum ( id) ");
+//        printAttr("  sum ( id  ) ");
+//        printAttr("abs(sum(id))");
+//        printAttr("abs (sum(id  ))");
+//        printAttr("count(distinct id)");
+//        printAttr("  count   (   distinct   id  )  ");
+//        printAttr("  count   (   distinct   distinct  )  ");
+//        printAttr("  count   (   distinct   as  )  ");
+//        printAttr("((  count   (   distinct   as  )  ))");
+//        printAttr("sum(curdate.sum)");
+//        printAttr("abs(as)");
+//
+//    }
+//
+//    private static void printClean(String str)
+//    {
+//        AttributeTokenizer t = new AttributeTokenizer(str);
+//        System.out.println("str: '" + t.attribute + "' --> '" + t.getCleanPath()+"'");
+//    }
+//
+//    private static void printAttr(String str)
+//    {
+//        AttributeTokenizer t = new AttributeTokenizer(str);
+//        System.out.println("## String: " + t.attribute);
+//        while(t.hasNext())
+//        {
+//            System.out.println("hasNext=" + t.hasNext() + ", nextIsAttr=" + t.nextIsAttribute()+
", next=" + t.next()
+//                    + ", current=" + t.current() + ", currentIsAttr=" + t.currentIsAttribute());
+//        }
+//        System.out.println("--------------------------");
+//    }
+
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org


Mime
View raw message