Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.zoodb.jdo.QueryImpl Maven / Gradle / Ivy
/*
* Copyright 2009-2013 Tilmann Zaeschke. All rights reserved.
*
* This file is part of ZooDB.
*
* ZooDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ZooDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ZooDB. If not, see .
*
* See the README and COPYING files for further information.
*/
package org.zoodb.jdo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.jdo.Extent;
import javax.jdo.FetchPlan;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import org.zoodb.api.impl.ZooPCImpl;
import org.zoodb.jdo.internal.ZooClassProxy;
import org.zoodb.jdo.internal.Node;
import org.zoodb.jdo.internal.ZooClassDef;
import org.zoodb.jdo.internal.query.QueryAdvice;
import org.zoodb.jdo.internal.query.QueryOptimizer;
import org.zoodb.jdo.internal.query.QueryParameter;
import org.zoodb.jdo.internal.query.QueryParser;
import org.zoodb.jdo.internal.query.QueryTerm;
import org.zoodb.jdo.internal.query.QueryTreeIterator;
import org.zoodb.jdo.internal.query.QueryTreeNode;
import org.zoodb.jdo.internal.util.CloseableIterator;
import org.zoodb.jdo.internal.util.DBLogger;
import org.zoodb.jdo.internal.util.ObjectIdentitySet;
/**
* Query implementation.
*
* @author Tilmann Zaeschke
*/
public class QueryImpl implements Query {
/** default. */
private static final long serialVersionUID = 1L;
// transient to satisfy findbugs (Query is Serializable, but _pm / _ext are not).
private transient PersistenceManagerImpl pm;
private transient Extent> ext;
private boolean isUnmodifiable = false;
private Class> candCls = ZooPCImpl.class; //TODO good default?
private transient ZooClassDef candClsDef = null;
private List indexToUse = null;
private String filter = "";
private boolean unique = false;
private boolean subClasses = true;
private boolean ascending = true;
private boolean ignoreCache = true;
private String resultSettings = null;
private Class> resultClass = null;
private final ObjectIdentitySet queryResults = new ObjectIdentitySet();
private List parameters = new LinkedList();
private QueryTreeNode queryTree;
@SuppressWarnings("rawtypes")
public QueryImpl(PersistenceManagerImpl pm, Extent ext, String filter) {
this(pm);
this.ext = ext;
setClass( this.ext.getCandidateClass() );
this.filter = filter;
compileQuery();
}
@SuppressWarnings("rawtypes")
public QueryImpl(PersistenceManagerImpl pm, Class cls,
String arg1) {
this(pm);
setClass( cls );
this.filter = arg1;
compileQuery();
}
public QueryImpl(PersistenceManagerImpl pm) {
this.pm = pm;
ignoreCache = pm.getIgnoreCache();
}
/**
* SELECT [UNIQUE] [] [INTO ]
[FROM [EXCLUDE SUBCLASSES]]
[WHERE ]
[VARIABLES ]
[PARAMETERS ]
[]
[GROUP BY ]
[ORDER BY ]
[RANGE , ]
* @param pm
* @param arg0
*/
public QueryImpl(PersistenceManagerImpl pm, String arg0) {
this.pm = pm;
if (arg0==null || arg0 == "") {
throw new NullPointerException("Please provide a query string.");
}
StringTokenizer st = new StringTokenizer(arg0);
String q = arg0.trim();
String tok = st.nextToken();
//SELECT
if (!tok.toLowerCase().equals("select")) {
throw new JDOUserException("Illegal token in query: \"" + tok + "\"");
}
q = q.substring(6).trim();
tok = st.nextToken();
//UNIQUE
if (tok.toLowerCase().equals("unique")) {
unique = true;
q = q.substring(6).trim();
tok = st.nextToken();
}
//INTO
//TODO
if (tok.toLowerCase().equals("into")) {
// _unique = true;
// q = q.substring(4).trim();
// tok = getToken(q);
System.err.println("Query not supported: INTO");
}
//FROM
if (tok.toLowerCase().equals("from")) {
q = q.substring(4).trim();
tok = st.nextToken();
setClass( locateClass(tok) );
q = q.substring(tok.length()).trim();
if (!st.hasMoreTokens()) {
return;
}
tok = st.nextToken();
//EXCLUDE SUBCLASSES
if (tok.toLowerCase().equals("exclude")) {
q = q.substring(7).trim();
tok = st.nextToken();
if (!tok.toLowerCase().equals("subclasses")) {
throw new JDOUserException("Illegal token in query, expected 'SUBCLASSES': \"" +
tok + "\"");
}
subClasses = false;
q = q.substring(7).trim();
tok = st.nextToken();
}
}
//WHERE
if (tok.toLowerCase().equals("where")) {
q = q.substring(5).trim();
this.filter = q;
//TODO
} else {
//maybe the query is finished?
if (!tok.toLowerCase().equals("")) {
throw new JDOUserException("Illegal token in query, expected 'WHERE': \"" + tok +
"\"");
}
}
compileQuery();
}
private Class> locateClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new JDOUserException("Class not found: " + className, e);
}
}
@Override
public void addExtension(String key, Object value) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void addSubquery(Query sub, String variableDeclaration,
String candidateCollectionExpression) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void addSubquery(Query sub, String variableDeclaration,
String candidateCollectionExpression, String parameter) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void addSubquery(Query sub, String variableDeclaration,
String candidateCollectionExpression, String... parameters) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@SuppressWarnings("rawtypes")
@Override
public void addSubquery(Query sub, String variableDeclaration,
String candidateCollectionExpression, Map parameters) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void close(Object queryResult) {
Object qr = queryResults.remove(queryResult);
if (qr == null) {
//TODO what does JDO say about this?
DBLogger.debugPrintln(0, "QueryResult not found.");
return;
}
if (qr instanceof ExtentAdaptor) {
((ExtentAdaptor>)qr).closeAll();
} else if (qr instanceof ExtentImpl) {
((ExtentImpl>)qr).closeAll();
} else {
//TODO ignore this
DBLogger.debugPrintln(0, "QueryResult not closable.");
}
}
@Override
public void closeAll() {
while (!queryResults.isEmpty()) {
close(queryResults.iterator().next());
}
}
@Override
public void compile() {
checkUnmodifiable(); //? TODO ?
//TODO compile only if changed?
compileQuery();
}
private void compileQuery() {
if (filter == null || filter.length() == 0) {
return;
}
//TODO compile only if it was not already compiled, unless the filter changed...
//We do this on the query before assigning values to parameter.
//Would it make sense to assign the values first and then properly parse the query???
//Probably not:
//- every parameter change would require rebuilding the tree
//- we would require an additional parser to assign the parameters
parameters.clear(); //See Test_122: We need to clear this for setFilter() calls
QueryParser qp = new QueryParser(filter, candClsDef, parameters);
queryTree = qp.parseQuery();
}
@Override
public void declareImports(String imports) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
/**
* For example:
* Query q = pm.newQuery (Employee.class, "salary > sal && name.startsWith(begin");
* q.declareParameters ("Float sal, String begin");
*/
@Override
public void declareParameters(String parameters) {
checkUnmodifiable();
parameters = parameters.trim();
int i1 = parameters.indexOf(',');
while (i1 >= 0) {
String p1 = parameters.substring(0, i1).trim();
updateParameterDeclaration(p1);
parameters = parameters.substring(i1+1, parameters.length()).trim();
i1 = parameters.indexOf(',');
}
updateParameterDeclaration(parameters);
}
private void updateParameterDeclaration(String paramDecl) {
int i = paramDecl.indexOf(' ');
String type = paramDecl.substring(0, i);
String name = paramDecl.substring(i+1);
if (name.startsWith(":")) {
throw new JDOUserException("Illegal parameter name: " + name);
}
for (QueryParameter p: parameters) {
if (p.getName().equals(name)) {
if (p.getType() != null) {
throw new JDOUserException("Duplicate parameter name: " + name);
}
p.setType(type);
return;
}
}
throw new JDOUserException("Parameter not used in query: " + name);
}
@Override
public void declareVariables(String variables) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public long deletePersistentAll() {
checkUnmodifiable(); //?
// if (_ext != null && (_filter == null || _filter.isEmpty())) {
// //deleting extent only
// Session s = _pm.getSession();
// int size = 0;
// long oid;
// //TODO
// //improve: do not iterate OIDs
// // instead: send class-delete command to Database
// // for cached objects, used list of cached objects in ZooClassDef to identify
// // them and mark them as deleted.
// // But: How do we prevent objects from appearing in queries? A flag in ZooCLassDef
// // would only work if implement special treatment for objects that are created afterwards (??)
// while ((oid=_ext.nextOid()) != Session.OID_NOT_ASSIGNED) {
// size++;
// //TODO
// //delete oid
// }
// _pm.deletePersistentAll(c);
// return size;
// }
Collection> c = (Collection>) execute();
int size = 0;
for (Object o: c) {
size++;
}
pm.deletePersistentAll(c);
return size;
}
@Override
public long deletePersistentAll(Object... parameters) {
checkUnmodifiable(); //?
Collection> c = (Collection>) executeWithArray(parameters);
int size = 0;
for (Object o: c) {
size++;
}
pm.deletePersistentAll(c);
return size;
}
@SuppressWarnings("rawtypes")
@Override
public long deletePersistentAll(Map parameters) {
checkUnmodifiable(); //?
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
private void assignParametersToQueryTree(QueryTreeNode queryTree) {
QueryTreeIterator iter = queryTree.termIterator();
while (iter.hasNext()) {
QueryTerm term = iter.next();
if (!term.isParametrized()) {
continue;
}
String pName = term.getParamName();
//TODO cache Fields in QueryTerm to avoid String comparison?
boolean isAssigned = false;
for (QueryParameter param: parameters) {
if (pName.equals(param.getName())) {
//TODO assigning a parameter instead of the value means that the query will
//adapt new values even if it is not recompiled.
term.setParameter(param);
//check
if (param.getType() == null) {
throw new JDOUserException(
"Parameter has not been declared: " + param.getName());
}
isAssigned = true;
break;
}
}
//TODO Exception?
if (!isAssigned) {
System.out.println("WARNING: Query parameter is not assigned: \"" + pName + "\"");
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void applyQueryOnExtent(List ret, QueryAdvice qa) {
QueryTreeNode queryTree = qa.getQuery();
Iterator> ext2;
if (qa.getIndex() != null) {
//TODO other nodes...
ext2 = pm.getSession().getPrimaryNode().readObjectFromIndex(qa.getIndex(),
qa.getMin(), qa.getMax(), !ignoreCache);
//System.out.println("Index: " + qa.getIndex().getName() + " " + qa.getMin() + "/" + qa.getMax());
} else {
//use extent
if (ext != null) {
//use user-defined extent
ext2 = ext.iterator();
} else {
//create type extent
ext2 = new ExtentImpl(candCls, subClasses, pm, ignoreCache).iterator();
}
}
if (ext != null && (!ext.hasSubclasses() || !ext.getCandidateClass().isAssignableFrom(candCls))) {
final boolean hasSub = ext.hasSubclasses();
final Class supCls = ext.getCandidateClass();
// normal iteration
while (ext2.hasNext()) {
Object o = ext2.next();
if (hasSub) {
if (!supCls.isAssignableFrom(o.getClass())) {
continue;
}
} else {
if (supCls != o.getClass()) {
continue;
}
}
boolean isMatch = queryTree.evaluate(o);
if (isMatch) {
ret.add(o);
}
}
} else {
// normal iteration (ignoring the possibly existing compatible extent to allow indices)
while (ext2.hasNext()) {
Object o = ext2.next();
boolean isMatch = queryTree.evaluate(o);
if (isMatch) {
ret.add(o);
}
}
}
if (ext2 instanceof CloseableIterator) {
((CloseableIterator)ext2).close();
}
}
private void checkParamCount(int i) {
//this needs to be checked AFTER query compilation
int max = parameters.size();
if (i > max) {
throw new JDOUserException("Too many arguments given, parameter count: " + max);
}
if (i < max) {
throw new JDOUserException("Too few arguments given, parameter count: " + max);
}
}
private Object runQuery() {
//assign parameters
assignParametersToQueryTree(queryTree);
//This is only for indices, not for given extents
QueryOptimizer qo = new QueryOptimizer(candClsDef);
indexToUse = qo.determineIndexToUse(queryTree);
//TODO can also return a list with (yet) unknown size. In that case size() should return
//Integer.MAX_VALUE (JDO 2.2 14.6.1)
ArrayList ret = new ArrayList();
for (QueryAdvice qa: indexToUse) {
applyQueryOnExtent(ret, qa);
}
//Now check if we need to check for duplicates, i.e. if multiple indices were used.
for (QueryAdvice qa: indexToUse) {
if (qa.getIndex() != indexToUse.get(0).getIndex()) {
DBLogger.debugPrintln(0, "Merging query results(A)!");
System.out.println("Merging query results(A)!");
Set ret2 = new ObjectIdentitySet();
ret2.addAll(ret);
return postProcess(ret2);
}
}
//If we have more than one sub-query, we need to merge anyway, because the result sets may
//overlap.
//TODO implement merging of sub-queries!!!
if (indexToUse.size() > 1) {
DBLogger.debugPrintln(0, "Merging query results(B)!");
System.out.println("Merging query results(B)!");
Set ret2 = new ObjectIdentitySet();
ret2.addAll(ret);
return postProcess(ret2);
}
return postProcess(ret);
}
@SuppressWarnings("unchecked")
private Object postProcess(Collection c) {
if (resultSettings != null) {
QueryResultProcessor rp =
new QueryResultProcessor(resultSettings, candCls, candClsDef, resultClass);
Object o = rp.processResult(c, unique);
if (!(o instanceof Collection)) {
//must be an aggregate
return o;
}
c = (Collection) o;
}
if (unique) {
//unique
if (c.isEmpty()) {
return null;
} else if (c.size() == 1) {
return c.iterator().next();
}
throw new JDOUserException("Too many results found in unique query.");
}
return c;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Object execute() {
//now go through extent. Skip this if extent was generated on server from local filters.
if (filter.equals("")) {
if (ext == null) {
ext = new ExtentImpl(candCls, subClasses, pm, ignoreCache);
}
return postProcess(new ExtentAdaptor(ext));
}
checkParamCount(0);
return runQuery();
}
/**
* {@inheritDoc}
*/
@Override
public Object execute(Object p1) {
return executeWithArray(p1);
}
/**
* {@inheritDoc}
*/
@Override
public Object execute(Object p1, Object p2) {
return executeWithArray(p1, p2);
}
/**
* {@inheritDoc}
*/
@Override
public Object execute(Object p1, Object p2, Object p3) {
return executeWithArray(p1, p2, p3);
}
/**
* {@inheritDoc}
*/
@Override
public Object executeWithArray(Object... parameters) {
checkParamCount(parameters.length);
for (int i = 0; i < parameters.length; i++) {
this.parameters.get(i).setValue(parameters[i]);
}
return runQuery();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("rawtypes")
@Override
public Object executeWithMap(Map parameters) {
checkParamCount(parameters.size());
for (QueryParameter p: this.parameters) {
p.setValue(parameters.get(p.getName()));
}
return runQuery();
}
@Override
public FetchPlan getFetchPlan() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public boolean getIgnoreCache() {
return ignoreCache;
}
@Override
public PersistenceManager getPersistenceManager() {
return pm;
}
@Override
public boolean isUnmodifiable() {
return isUnmodifiable;
}
@SuppressWarnings("rawtypes")
@Override
public void setCandidates(Extent pcs) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@SuppressWarnings("rawtypes")
@Override
public void setCandidates(Collection pcs) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("rawtypes")
@Override
public void setClass(Class cls) {
checkUnmodifiable();
if (!ZooPCImpl.class.isAssignableFrom(cls)) {
throw new JDOUserException("Class is not persistence capabale: " + cls.getName());
}
candCls = cls;
Node node = pm.getSession().getPrimaryNode();
ZooClassProxy sch = pm.getSession().getSchemaManager().locateSchema(cls, node);
if (sch == null) {
throw new JDOUserException("Class schema is not defined: " + cls.getName());
}
candClsDef = sch.getSchemaDef();
}
@SuppressWarnings("rawtypes")
@Override
public void setExtensions(Map extensions) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void setFilter(String filter) {
checkUnmodifiable();
this.filter = filter;
compileQuery();
}
@Override
public void setGrouping(String group) {
checkUnmodifiable();
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void setIgnoreCache(boolean ignoreCache) {
this.ignoreCache = ignoreCache;;
}
@Override
public void setOrdering(String ordering) {
checkUnmodifiable();
ascending = true;
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void setRange(String fromInclToExcl) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void setRange(long fromIncl, long toExcl) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void setResult(String data) {
checkUnmodifiable();
//we can't check this here, because the filter and candidate class may still change
resultSettings = data;
}
@SuppressWarnings("rawtypes")
@Override
public void setResultClass(Class cls) {
checkUnmodifiable();
resultClass = cls;
}
@Override
public void setUnique(boolean unique) {
checkUnmodifiable();
this.unique = unique;
}
@Override
public void setUnmodifiable() {
isUnmodifiable = true;
}
private void checkUnmodifiable() {
if (isUnmodifiable) {
throw new JDOUserException("This query is unmodifiable.");
}
}
@Override
public void cancel(Thread arg0) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//
}
@Override
public void cancelAll() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//
}
@Override
public Integer getDatastoreReadTimeoutMillis() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//return null;
}
@Override
public Integer getDatastoreWriteTimeoutMillis() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//return null;
}
@Override
public Boolean getSerializeRead() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//return null;
}
@Override
public void setDatastoreReadTimeoutMillis(Integer arg0) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//
}
@Override
public void setDatastoreWriteTimeoutMillis(Integer arg0) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//
}
@Override
public void setSerializeRead(Boolean arg0) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
//
}
}