/*
* #!
* 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.utils;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import net.ontopia.utils.OntopiaRuntimeException;
import net.ontopia.topicmaps.core.TopicIF;
import net.ontopia.topicmaps.core.TopicMapIF;
import net.ontopia.topicmaps.query.utils.QueryUtils;
import net.ontopia.topicmaps.query.core.QueryResultIF;
import net.ontopia.topicmaps.query.core.QueryProcessorIF;
import net.ontopia.topicmaps.query.core.DeclarationContextIF;
import net.ontopia.topicmaps.query.core.InvalidQueryException;
/**
* EXPERIMENTAL: Class a la Spring's JDBC templates to simplify use of
* the tolog query engine API.
* @since 3.4.4
*/
public class QueryWrapper {
private TopicMapIF topicmap;
private DeclarationContextIF context;
private QueryProcessorIF processor;
/**
* EXPERIMENTAL: Creates a wrapper for this particular topic map.
*/
public QueryWrapper(TopicMapIF topicmap) {
this.topicmap = topicmap;
this.processor = QueryUtils.getQueryProcessor(topicmap);
}
public QueryProcessorIF getQueryProcessor() {
return processor;
}
public DeclarationContextIF getDeclarationContext() {
return context;
}
public void setDeclarationContext(DeclarationContextIF context) {
this.context = context;
}
/**
* EXPERIMENTAL: Sets the parsing context for the query processor.
* Each call to this method overwrites the results from previous
* calls.
* @param declarations a tolog fragment containing prefix declarations
*/
public void setDeclarations(String declarations) {
try {
if (context == null)
context = QueryUtils.parseDeclarations(topicmap, declarations);
else
context = QueryUtils.parseDeclarations(topicmap, declarations, context);
} catch (InvalidQueryException e) {
throw new OntopiaRuntimeException(e);
}
}
/**
* EXPERIMENTAL: Utility method for creating parameter maps.
*/
public Map makeParams(String name, Object value) {
Map params = new HashMap(1);
params.put(name, value);
return params;
}
/**
* EXPERIMENTAL: Runs the query, and returns a the single value in
* each row.
*/
public List queryForList(String query) {
return queryForList(query, new ObjectMapper(false), null);
}
/**
* EXPERIMENTAL: Runs the query, and returns a the single value in
* each row.
*/
public List queryForList(String query, Map params) {
return queryForList(query, new ObjectMapper(false), params);
}
/**
* EXPERIMENTAL: Runs the query, and calls the mapper for each row
* in the query result. Returns a list of all the objects produced
* by the mapper in query result order.
*/
public List queryForList(String query, RowMapperIF mapper) {
return queryForList(query, mapper, null);
}
/**
* EXPERIMENTAL: Runs the query with the given parameters, and calls
* the mapper for each row in the query result. Returns a list of
* all the objects produced by the mapper in query result order.
*/
public List queryForList(String query, RowMapperIF mapper, Map params) {
List list = new ArrayList();
QueryResultIF result = null;
try {
result = processor.execute(query, params, context);
int ix = 0;
while (result.next())
list.add(mapper.mapRow(result, ix++));
} catch (InvalidQueryException e) {
throw new OntopiaRuntimeException(e);
} finally {
if (result != null)
result.close();
}
return list;
}
/**
* EXPERIMENTAL: Returns a map of the first row of the query
* results, with each variable name (without $) as a key and each
* variable value as the value of the key. If the query produces no
* rows the method returns null; if it produces more than one an
* exception is thrown.
*/
public Map queryForMap(String query) {
return queryForMap(query, null);
}
/**
* EXPERIMENTAL: Returns a map of the first row of the query
* results, with each variable name (without $) as a key and each
* variable value as the value of the key. If the query produces no
* rows the method returns null; if it produces more than one an
* exception is thrown.
*/
public Map queryForMap(String query, Map params) {
MapMapper mapper = new MapMapper();
List list = queryForList(query, mapper, params);
int size = list.size();
if (size == 0)
return null;
else if (size == 1)
return (Map) list.get(0);
else
throw new OntopiaRuntimeException("Query produced more than one row");
}
/**
* EXPERIMENTAL: Returns a list of Map<String, Object> values for
* each row in the query, with the variable name as the key.
* @since 5.1.0
*/
public List queryForMaps(String query) {
return queryForMaps(query, null);
}
/**
* EXPERIMENTAL: Returns a list of Map<String, Object> values for
* each row in the query, with the variable name as the key.
*/
public List queryForMaps(String query, Map params) {
return queryForList(query, new MapMapper(false), params);
}
private class MapMapper implements RowMapperIF {
private boolean maxone;
public MapMapper() {
}
public MapMapper(boolean maxone) {
this.maxone = maxone;
}
public Object mapRow(QueryResultIF result, int rowno) {
if (maxone && rowno == 1)
throw new OntopiaRuntimeException("Query produced more than one row");
Map row = new HashMap(result.getWidth());
for (int ix = 0; ix < result.getWidth(); ix++)
row.put(result.getColumnName(ix), result.getValue(ix));
return row;
}
}
/**
* EXPERIMENTAL: Returns true if the query produces a row and
* false if the query produces no rows. If the query produces
* more than one row an exception is thrown.
*/
public boolean isTrue(String query) {
return isTrue(query, null);
}
/**
* EXPERIMENTAL: Returns true if the query produces a row and
* false if the query produces no rows. If the query produces
* more than one row an exception is thrown.
*/
public boolean isTrue(String query, Map params) {
List list = queryForList(query, new RowMapperIF(){
public Object mapRow(QueryResultIF result, int rowno) {
if (rowno == 1)
throw new OntopiaRuntimeException("Query produced more than one row");
return new Object();
}
}, params);
return !list.isEmpty();
}
/**
* EXPERIMENTAL: Returns a String from the first column in the first
* row of the query result. If the query produces no results null is
* returned. If the query produces more than one row or more than
* one column, an exception is thrown.
*/
public String queryForString(String query) {
return queryForString(query, null);
}
/**
* EXPERIMENTAL: Returns a String from the first column in the first
* row of the query result. If the query produces no results null is
* returned. If the query produces more than one row or more than
* one column, an exception is thrown.
*/
public String queryForString(String query, Map params) {
return (String) queryForObject(query, params);
}
/**
* EXPERIMENTAL: Returns a topic from the first column in the first
* row of the query result. If the query produces no results null is
* returned. If the query produces more than one row or more than
* one column, an exception is thrown.
*/
public TopicIF queryForTopic(String query) {
return queryForTopic(query, null);
}
/**
* EXPERIMENTAL: Returns a topic from the first column in the first
* row of the query result. If the query produces no results null is
* returned. If the query produces more than one row or more than
* one column, an exception is thrown.
*/
public TopicIF queryForTopic(String query, Map params) {
return (TopicIF) queryForObject(query, params);
}
/**
* EXPERIMENTAL: Returns the value in the first column in the first
* row of the query result. If the query produces no results null is
* returned. If the query produces more than one row or more than
* one column, an exception is thrown.
*/
public Object queryForObject(String query) {
return queryForObject(query, null, null);
}
/**
* EXPERIMENTAL: Returns the mapping of the value in the first
* column in the first row of the query result. If the query
* produces no results null is returned. If the query produces more
* than one row or more than one column, an exception is thrown.
*/
public Object queryForObject(String query, RowMapperIF mapper) {
return queryForObject(query, mapper, null);
}
/**
* EXPERIMENTAL: Returns the value in the first column in the first
* row of the query result. If the query produces no results null is
* returned. If the query produces more than one row or more than
* one column, an exception is thrown.
*/
public Object queryForObject(String query, Map params) {
return queryForObject(query, new ObjectMapper(), params);
}
/**
* EXPERIMENTAL: Returns the mapping of the value in the first
* column in the first row of the query result. If the query
* produces no results null is returned. If the query produces more
* than one row or more than one column, an exception is thrown.
*/
public Object queryForObject(String query, RowMapperIF mapper, Map params) {
List list = queryForList(query, mapper, params);
if (list.isEmpty())
return null;
else
return list.iterator().next();
}
class ObjectMapper implements RowMapperIF {
private boolean maxone;
public ObjectMapper() {
this.maxone = true;
}
public ObjectMapper(boolean maxone) {
this.maxone = maxone;
}
public Object mapRow(QueryResultIF result, int rowno) {
if (maxone && rowno == 1)
throw new OntopiaRuntimeException("Query produced more than one row");
return result.getValue(0);
}
}
/**
* EXPERIMENTAL: Runs an update statement, returning the number of
* changed rows. Makes no attempt to handle transactions, as this is
* an application issue.
* @since 5.1.1
*/
public int update(String query) {
return update(query, null);
}
/**
* EXPERIMENTAL: Runs an update statement, returning the number of
* changed rows. Makes no attempt to handle transactions, as this is
* an application issue.
* @since 5.1.1
*/
public int update(String query, Map params) {
try {
return processor.update(query, params, context);
} catch (InvalidQueryException e) {
throw new OntopiaRuntimeException(e);
}
}
}