/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* 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.
* !#
*/
package net.ontopia.persistence.proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.ontopia.persistence.query.sql.DefaultParameterProcessor;
import net.ontopia.persistence.query.sql.DetachedQueryIF;
import net.ontopia.persistence.query.sql.RDBMSCollectionQuery;
import net.ontopia.persistence.query.sql.RDBMSMapQuery;
import net.ontopia.persistence.query.sql.RDBMSMatrixQuery;
import net.ontopia.persistence.query.sql.RDBMSObjectQuery;
import net.ontopia.persistence.query.sql.RDBMSQuery;
import net.ontopia.persistence.query.sql.SQLStatement;
import net.ontopia.persistence.query.sql.SQLStatementIF;
import net.ontopia.utils.OntopiaRuntimeException;
/**
* INTERNAL: Class used for loading and managing SQL query
* declarations. It is used by the rdbms proxy implementation, but
* should implement a query factory interface in the future.
*/
public class QueryDescriptor {
protected String name;
protected String type;
protected int fetchSize;
protected SelectField[] selects;
protected Class[] params;
protected Map<String, String> statements;
protected boolean lookup_identities;
public QueryDescriptor(String name, String type, boolean lookup_identities) {
this.name = name;
this.type = type;
this.statements = new HashMap<String, String>();
this.lookup_identities = lookup_identities;
}
/**
* INTERNAL: Returns the name of the query. This field is used
* together with named query execution.
*/
public String getName() {
return name;
}
/**
* INTERNAL: Gets the query result type name.
*/
public String getType() {
return type;
}
/**
* INTERNAL: Gets the default fetch size.
*/
public int getFetchSize() {
return fetchSize;
}
/**
* INTERNAL: Sets the default fetch size.
*/
public void setFetchSize(int fetchSize) {
this.fetchSize = fetchSize;
}
/**
* INTERNAL: Sets the selected fields.
*/
public void setSelects(List<SelectField> selects) {
this.selects = new SelectField[selects.size()];
selects.toArray(this.selects);
}
/**
* INTERNAL: Sets the class type of the query parameters.
*/
public void setParameters(List<Class<?>> params) {
this.params = new Class[params.size()];
params.toArray(this.params);
}
/**
* INTERNAL: Gets the class type of the query parameters.
*/
public String getStatement(String[] platforms) {
for (int i=0; i < platforms.length; i++) {
if (statements.containsKey(platforms[i])) {
return statements.get(platforms[i]);
}
}
throw new OntopiaRuntimeException("No statement available for query '" + getName() +
"' (platforms: " + Arrays.asList(platforms) + ")");
}
/**
* INTERNAL: Registers the query statement for the specified
* platforms.
*/
public void addStatement(String[] platforms, String statement) {
for (int i=0; i < platforms.length; i++) {
if (statements.containsKey(platforms[i]))
throw new OntopiaRuntimeException("Duplicate statements for '" + platforms[i] + "' (query: " + getName() + ")");
statements.put(platforms[i], statement);
}
}
/**
* INTERNAL: Creates a QueryIF instance that uses the specified
* storage access and platform settings.
*/
public DetachedQueryIF createSharedQuery(StorageIF storage, AccessRegistrarIF registrar, String[] platforms) {
SQLStatement stm = createSQLStatement(storage, platforms);
stm.setAccessRegistrar(registrar);
return createDetachedQuery(stm, getType(), false);
}
/**
* INTERNAL: Creates a QueryIF instance that uses the specified
* storage access, object access, access registrar and platform
* settings for this query descriptor.
*/
public QueryIF createQuery(RDBMSAccess access, ObjectAccessIF oaccess,
AccessRegistrarIF registrar, String[] platforms) {
SQLStatement stm = createSQLStatement(access.getStorage(), platforms);
stm.setObjectAccess(oaccess);
stm.setAccessRegistrar(registrar);
DetachedQueryIF query = createDetachedQuery(stm, getType(), this.lookup_identities);
return new RDBMSQuery(access, query);
}
protected SQLStatement createSQLStatement(StorageIF storage, String[] platforms) {
ObjectRelationalMappingIF mapping = storage.getMapping();
FieldHandlerIF[] select_fields = getSelectFieldHandlers(mapping, selects);
FieldHandlerIF[] param_fields = getParameterHandlers(mapping, params);
String[] param_names = null; // TODO: Add support for parameter names
DefaultParameterProcessor param_proc =
new DefaultParameterProcessor(param_fields, param_names);
if (fetchSize > 0) param_proc.setFetchSize(fetchSize);
SQLStatement stm = new SQLStatement(getStatement(platforms), select_fields, param_proc);
if (fetchSize > 0) stm.setFetchSize(fetchSize);
return stm;
}
protected DetachedQueryIF createDetachedQuery(SQLStatementIF stm, String type, boolean lookup_identities) {
if (type.equals("object"))
return new RDBMSObjectQuery(stm, lookup_identities);
else if (type.equals("collection"))
return new RDBMSCollectionQuery(stm, lookup_identities);
else if (type.equals("matrix"))
return new RDBMSMatrixQuery(stm, lookup_identities);
else if (type.equals("map"))
return new RDBMSMapQuery(stm, lookup_identities);
else
throw new OntopiaRuntimeException("Invalid query type: " + type);
}
protected FieldHandlerIF[] getSelectFieldHandlers(ObjectRelationalMappingIF mapping, SelectField[] selects) {
FieldHandlerIF[] handlers = new FieldHandlerIF[selects.length];
for (int i=0; i < selects.length; i++) {
handlers[i] = getSelectFieldHandler(mapping, selects[i]);
}
return handlers;
}
protected FieldHandlerIF getSelectFieldHandler(ObjectRelationalMappingIF mapping, SelectField select) {
switch (select.getType()) {
case SelectField.SELECT_CLASS:
return getFieldHandler(mapping, (Class)select.getValue());
case SelectField.SELECT_INDICATOR:
return new IndicatorFieldHandler(mapping, (Map)select.getValue());
default:
throw new OntopiaRuntimeException("Select field is invalid: " + select);
}
}
protected FieldHandlerIF[] getParameterHandlers(ObjectRelationalMappingIF mapping, Class[] params) {
FieldHandlerIF[] handlers = new FieldHandlerIF[params.length];
for (int i=0; i < params.length; i++) {
handlers[i] = getFieldHandler(mapping, params[i]);
}
return handlers;
}
protected FieldHandlerIF getFieldHandler(ObjectRelationalMappingIF mapping, Class klass) {
if (mapping.isDeclared(klass)) {
ClassInfoIF cinfo = mapping.getClassInfo(klass);
if (cinfo.isIdentifiable()) {
return cinfo.getIdentityFieldInfo();
// FIXME: Also handle aggregate types
//! else if (cinfo.isAggregate()) {
//! handlers[i] = cinfo.getAggregateF();
}
else {
throw new OntopiaRuntimeException("Parameter class has invalid descriptor: " + klass);
}
} else {
return new DefaultFieldHandler(SQLTypes.getType(klass));
}
}
static class SelectField {
public static final int SELECT_CLASS = 1;
public static final int SELECT_INDICATOR = 2;
public static final int SELECT_FACTORY = 3;
protected Object value;
protected int type;
public SelectField(Object value, int type) {
this.value = value;
this.type = type;
}
public int getType() {
return type;
}
public Object getValue() {
return value;
}
public String toString() {
return "<SelectField " + type + " " + value + ">";
}
}
}