Author: arminw
Date: Tue Mar 20 19:27:10 2007
New Revision: 520720
URL: http://svn.apache.org/viewvc?view=rev&rev=520720
Log:
fix for OJB-133
Added:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java
Modified:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java
Modified:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java
URL:
http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java?view=diff&rev=520720&r1=520719&r2=520720
==============================================================================
---
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java
(original)
+++
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/accesslayer/sql/SqlQueryStatement.java
Tue Mar 20 19:27:10 2007
@@ -22,8 +22,8 @@
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.PersistenceBrokerSQLException;
import org.apache.ojb.broker.accesslayer.JoinSyntaxTypes;
import org.apache.ojb.broker.metadata.ClassDescriptor;
@@ -48,7 +48,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;
@@ -107,7 +107,11 @@
public SqlQueryStatement(SqlQueryStatement parent, Platform pf,
ClassDescriptor cld, Query query, Logger logger)
{
super(pf, logger);
-
+ if(cld.isAbstract() && !query.getWithExtents())
+ {
+ throw new PersistenceBrokerException("Can't query objects of
abstract class/interface '"
+ + cld.getClassNameOfObject() + "' with disabled 'extents'
- Query.setWithExtents(false)");
+ }
m_parentStatement = parent;
m_query = (QueryByCriteria) query;
m_searchCld = cld;
@@ -186,14 +190,13 @@
*/
protected AttributeInfo getAttributeInfo(String attr, boolean
useOuterJoins, UserAlias aUserAlias, Map pathClasses)
{
+ AttributeTokenizer attrTok = new AttributeTokenizer(attr);
AttributeInfo result;
- StringTokenizer st = SqlHelper.tokenizeAttribute(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));
}
@@ -201,8 +204,9 @@
{
result.add(token);
}
+// System.out.println("attr=" + attr + ", current=" +
attrTok.current() + ", currentIsAttr="
+// + attrTok.currentIsAttribute());
}
-
return result;
}
@@ -360,18 +364,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;
@@ -1460,7 +1473,7 @@
Class clazz = (Class) iter.next();
Class superClazz = clazz.getSuperclass();
- if (superClazz != null &&
resultClass.equals(superClazz.getSuperclass()))
+ if (superClazz != null && resultClass != null &&
resultClass.equals(superClazz.getSuperclass()))
{
continue; // skip if we already have a super superclass
}
@@ -1727,7 +1740,7 @@
*/
private void buildJoinTreeForColumn(String aColName, boolean
useOuterJoin, UserAlias aUserAlias, Map pathClasses)
{
- String pathName = SqlHelper.cleanPath(aColName);
+ String pathName = new
AttributeTokenizer(aColName).getCleanPath();
int sepPos = pathName.lastIndexOf(".");
if (sepPos >= 0)
Modified:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java
URL:
http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java?view=diff&rev=520720&r1=520719&r2=520720
==============================================================================
---
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java
(original)
+++
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java
Tue Mar 20 19:27:10 2007
@@ -42,7 +42,7 @@
import org.apache.ojb.broker.locking.IsolationLevels;
import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
import org.apache.ojb.broker.util.ClassHelper;
-import org.apache.ojb.broker.util.SqlHelper;
+import org.apache.ojb.broker.util.AttributeTokenizer;
import org.apache.ojb.broker.util.configuration.Configuration;
import org.apache.ojb.broker.util.configuration.Configurator;
import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
@@ -841,10 +841,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.
@@ -867,18 +868,6 @@
return fld;
}
- /**
- * 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);
- }
/*
arminw:
TODO: this feature doesn't work, so remove this in future
@@ -1182,12 +1171,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)
@@ -1201,14 +1191,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);
}
/**
@@ -1217,7 +1208,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
Added:
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java
URL:
http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java?view=auto&rev=520720
==============================================================================
---
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java
(added)
+++
db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/util/AttributeTokenizer.java
Tue Mar 20 19:27:10 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]