/*
* #!
* 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.topicmaps.query.impl.rdbms;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.ontopia.persistence.query.jdo.JDOAggregate;
import net.ontopia.persistence.query.jdo.JDOAggregateIF;
import net.ontopia.persistence.query.jdo.JDONull;
import net.ontopia.persistence.query.jdo.JDOObject;
import net.ontopia.persistence.query.jdo.JDOParameter;
import net.ontopia.persistence.query.jdo.JDOQuery;
import net.ontopia.persistence.query.jdo.JDOString;
import net.ontopia.persistence.query.jdo.JDOValueIF;
import net.ontopia.persistence.query.jdo.JDOVariable;
import net.ontopia.topicmaps.query.parser.Pair;
import net.ontopia.topicmaps.query.parser.Parameter;
import net.ontopia.topicmaps.query.parser.TologQuery;
import net.ontopia.topicmaps.query.parser.Variable;
/**
* INTERNAL: Class used to hold context information when building JDO
* queries from tolog queries.
*/
public class QueryBuilder {
//! public BindingContext bc;
protected TologQuery query;
protected QueryBuilder parent;
protected Map attributes;
protected int vncounter = 1;
// Query variables {varalias : type}
// Temporary variables {varalias : type}
protected Map variables = new HashMap();
// Query parameters
protected Map params = new HashMap();
// Query processor
protected QueryProcessor qp;
QueryBuilder(TologQuery query, QueryProcessor qp) {
this(query, null, qp);
}
QueryBuilder(TologQuery query, QueryBuilder parent, QueryProcessor qp) {
this.query = query;
this.parent = parent;
this.qp = qp;
}
/* ---- Properties */
public String getProperty(String name) {
String propval = (qp == null ? null : qp.getProperty(name));
if (propval == null && parent != null)
return parent.getProperty(name);
else
return propval;
}
/* ---- Attributes */
public Object getAttribute(Object attr) {
if (attributes == null) return null;
return attributes.get(attr);
}
public void setAttribute(Object attr, Object value) {
if (attributes == null) attributes = new HashMap();
attributes.put(attr, value);
}
/* ---- Variables */
public Class getVariableType(String name) {
Object type = variables.get(name);
if (type != null && typemap.containsKey(type))
return (Class)typemap.get(type);
else
return (Class)type;
}
protected Class getVariableTypeFromParent(String name) {
if (parent == null) return null;
Class vtype = parent.getVariableType(name);
if (vtype != null)
return vtype;
else
return parent.getVariableTypeFromParent(name);
}
public Map getVariables() {
return variables;
}
public void setVariables(Map variables) {
this.variables = variables;
}
public String[] getVariableNames() {
String[] result = new String[variables.size()];
variables.keySet().toArray(result);
return result;
}
/* ---- Parameters */
public Class getParameterType(String name) {
Object type = params.get(name);
if (type != null && typemap.containsKey(type))
return (Class)typemap.get(type);
else
return (Class)type;
}
protected Class getParameterTypeFromParent(String name) {
if (parent == null) return null;
Class ptype = parent.getParameterType(name);
if (ptype != null)
return ptype;
else
return parent.getParameterTypeFromParent(name);
}
public Map getParameters() {
return params;
}
public void setParameters(Map params) {
this.params = params;
}
public String[] getParameterNames() {
String[] result = new String[params.size()];
params.keySet().toArray(result);
return result;
}
/* ---- Unsupported variables */
protected Set unsupported = new HashSet();
public boolean isSupportedVariable(Variable var) {
return !unsupported.contains(var);
}
public void addUnsupportedVariable(Variable var) {
unsupported.add(var);
}
// --- Registration of JDO query components
//! public void registerJDOVariables(JDOQuery jdoquery) {
//! // variables
//! if (variables.isEmpty()) return;
//! Iterator iter = variables.keySet().iterator();
//! while (iter.hasNext()) {
//! String varname = (String)iter.next();
//! Class vartype = getVariableType(varname);
//! if (vartype == null) {
//! vartype = getVariableTypeFromParent(varname);
//! }
//! jdoquery.addVariable(varname, vartype); // name : class
//! }
//! }
//!
//! public void registerJDOParameters(JDOQuery jdoquery) {
//! // parameters
//! if (params.isEmpty()) return;
//! Iterator iter = params.keySet().iterator();
//! while (iter.hasNext()) {
//! String parname = (String)iter.next();
//! Class partype = getParameterType(parname);
//! if (partype == null) {
//! partype = getParameterTypeFromParent(parname);
//! }
//! jdoquery.addParameter(parname, partype); // name : class
//! }
//! }
// selected : [ A, C, D ]
// all items : [ A, B, C, D, E, F ]
// clause items : [ B, C, D, F ] ==> [ null, B, C, D, null, F ]
public void registerJDOSelect(JDOQuery jdoquery, Set varnames, boolean aggfunc) {
// NOTE: query result being used directly (query not dependent on basic clauses)
Collection counted = query.getCountedVariables();
List selected = query.getSelectedVariables();
for (int ix = 0; ix < selected.size(); ix++) {
Variable var = (Variable)selected.get(ix);
String varname = var.getName();
// If variable is bound in query select it.
if (varnames.contains(varname)) {
if (aggfunc && counted.contains(var))
jdoquery.addSelect(new JDOAggregate(new JDOVariable(varname), JDOAggregateIF.COUNT));
else
jdoquery.addSelect(new JDOVariable(varname));
}
}
}
public void registerJDOSelectDependent(JDOQuery jdoquery, Set varnames) {
// NOTE: query result being passed on to basic clauses
// FIXME: no need to select those that are not needed elsewhere!
Iterator iter = varnames.iterator();
while (iter.hasNext()) {
JDOVariable jdovar = new JDOVariable((String)iter.next());
// select variable
jdoquery.addSelect(jdovar);
}
}
//! public void registerJDOSelect2(JDOQuery jdoquery, boolean aggfunc) {
//! // select
//! Collection counted = query.getCountedVariables();
//! List selected = query.getSelectedVariables();
//!
//! //! System.out.println("SELECTED VARS: " + selected);
//! // FIXME: Don't register if JDOQuery is set-query?
//! for (int ix = 0; ix < selected.size(); ix++) {
//! Variable var = (Variable)selected.get(ix);
//! String varname = var.getName();
//!
//! // If variable is bound in query select it.
//! if (variables.containsKey(varname)) {
//! if (aggfunc && counted.contains(var))
//! jdoquery.addSelect(new JDOAggregate(new JDOVariable(varname), JDOAggregateIF.COUNT));
//! else
//! jdoquery.addSelect(new JDOVariable(varname));
//! } else {
//! // Variable not bound, so we'll select null
//! jdoquery.addSelect(new JDONull());
//! }
//! }
//! //! System.out.println("|SELECTED VARS|: " + jdoquery.getSelect());
//! }
public void registerJDOOrderBy(JDOQuery jdoquery, boolean aggfunc) {
List orderby = query.getOrderBy();
Collection counted = query.getCountedVariables();
for (int ix = 0; ix < orderby.size(); ix++) {
Variable var = (Variable)orderby.get(ix);
String varname = var.getName();
JDOVariable jdovar = new JDOVariable(varname);
if (query.isOrderedAscending(varname)) {
if (aggfunc && counted.contains(var))
jdoquery.addAscending(new JDOAggregate(jdovar, JDOAggregateIF.COUNT));
else
jdoquery.addAscending(jdovar);
} else {
if (aggfunc && counted.contains(var))
jdoquery.addDescending(new JDOAggregate(jdovar, JDOAggregateIF.COUNT));
else
jdoquery.addDescending(jdovar);
}
}
}
// --- NEW METHODS OR METHODS TO KEEP
/**
* INTERNAL: Create a temporary variable.
*/
public JDOVariable createJDOVariable(String prefix, Class type) {
// NOTE: Make sure that the names don't collide with the proper
// variables
while (true) {
// Create new variable name
String varname = prefix + (vncounter++);
// Check if name collides with variable name
if (variables.containsKey(varname))
continue;
variables.put(varname, type);
return new JDOVariable(varname);
}
}
public JDOValueIF createJDOValue(Object argument) {
if (argument == null) {
return new JDONull();
} else if (argument instanceof Variable) {
// FIXME: have to map to variable ALIAS
String varname = ((Variable)argument).getName();
// TODO: is this neccessary?
Object vtype = getVariableType(varname);
if (vtype != null)
variables.put(varname, vtype);
return new JDOVariable(varname);
} else if (argument instanceof Parameter) {
String parname = ((Parameter)argument).getName();
return new JDOParameter(parname);
} else if (argument instanceof Pair) {
// Note: Pairs should be handled inside predicate.
throw new UnsupportedOperationException("Cannot create JDOValueIF from from Pair: " + argument);
} else if (argument instanceof String) {
return new JDOString((String)argument);
} else {
return new JDOObject(argument);
}
}
// --- Argument introspection
public boolean isArgumentOfType(Object argument, Class type) {
if (argument instanceof Variable) {
return type.isAssignableFrom(getVariableType(((Variable)argument).getName()));
} else if (argument instanceof Parameter) {
return type.isAssignableFrom(getParameterType(((Parameter)argument).getName()));
} else if (argument instanceof Pair) {
// Note: Pairs should be handled inside predicate.
throw new UnsupportedOperationException("Cannot figure out argument type from from Pair: " + argument);
} else {
return type.isAssignableFrom(argument.getClass());
}
}
public Class getArgumentType(Object argument) {
if (argument instanceof Variable) {
return getVariableType(((Variable)argument).getName());
} else if (argument instanceof Parameter) {
return getParameterType(((Parameter)argument).getName());
} else if (argument instanceof Pair) {
// Note: Pairs should be handled inside predicate.
throw new UnsupportedOperationException("Cannot figure out argument type from from Pair: " + argument);
} else {
return argument.getClass();
}
}
public static Map typemap = new HashMap();
static {
// The type map is used to map interface types to actual types used by the O/R-mapper
typemap.put(net.ontopia.topicmaps.core.AssociationIF.class, net.ontopia.topicmaps.impl.rdbms.Association.class);
typemap.put(net.ontopia.topicmaps.core.AssociationRoleIF.class, net.ontopia.topicmaps.impl.rdbms.AssociationRole.class);
typemap.put(net.ontopia.topicmaps.core.TopicNameIF.class, net.ontopia.topicmaps.impl.rdbms.TopicName.class);
typemap.put(net.ontopia.topicmaps.core.OccurrenceIF.class, net.ontopia.topicmaps.impl.rdbms.Occurrence.class);
typemap.put(net.ontopia.topicmaps.core.TopicIF.class, net.ontopia.topicmaps.impl.rdbms.Topic.class);
typemap.put(net.ontopia.topicmaps.core.TopicMapIF.class, net.ontopia.topicmaps.impl.rdbms.TopicMap.class);
typemap.put(net.ontopia.topicmaps.core.VariantNameIF.class, net.ontopia.topicmaps.impl.rdbms.VariantName.class);
}
}