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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] #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 [EMAIL PROTECTED] #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
+ * [EMAIL PROTECTED] #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: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]