/*
Copyright (C) 2003 EBI, GRL
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.ensembl.mart.shell;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ensembl.mart.lib.Attribute;
import org.ensembl.mart.lib.BasicFilter;
import org.ensembl.mart.lib.BooleanFilter;
import org.ensembl.mart.lib.DetailedDataSource;
import org.ensembl.mart.lib.FieldAttribute;
import org.ensembl.mart.lib.Filter;
import org.ensembl.mart.lib.IDListFilter;
import org.ensembl.mart.lib.InputSourceUtil;
import org.ensembl.mart.lib.InvalidQueryException;
import org.ensembl.mart.lib.Query;
import org.ensembl.mart.lib.SequenceDescription;
import org.ensembl.mart.lib.config.AttributeCollection;
import org.ensembl.mart.lib.config.AttributeDescription;
import org.ensembl.mart.lib.config.AttributeGroup;
import org.ensembl.mart.lib.config.AttributePage;
import org.ensembl.mart.lib.config.CompositeDSConfigAdaptor;
import org.ensembl.mart.lib.config.ConfigurationException;
import org.ensembl.mart.lib.config.DSConfigAdaptor;
import org.ensembl.mart.lib.config.DatabaseDSConfigAdaptor;
import org.ensembl.mart.lib.config.DatasetConfig;
import org.ensembl.mart.lib.config.DatasetConfigIterator;
import org.ensembl.mart.lib.config.FilterDescription;
import org.ensembl.mart.lib.config.FilterPage;
import org.ensembl.mart.lib.config.MultiDSConfigAdaptor;
import org.ensembl.mart.lib.config.RegistryDSConfigAdaptor;
import org.ensembl.mart.lib.config.URLDSConfigAdaptor;
/**
* <p>Library allowing client code to parse Mart Query Language (MQL)
* querries into Query Objects using MQLtoQuery, or parse Query objects into MQL querries using QueryToMQL.
*
* <!-- This is a css style for indenting lines below. It is placed here to allow the first line to be used in the package description. -->
* <STYLE>
* <!--
*
* P.indent_small {
* text-indent: 0.2in;
* }
*
* P.indent_big {
* text-indent: 0.5in;
* }
* -->
* </STYLE>
*
* <p>Mart Query Language (MQL) is a derivative of the Structured Query Language (SQL)
* used to query relational databases. It follows the following format (items in angle brackets are optional):</p>
* <p class="indent_big">< using datasetConfigName < dataSourceName > > </p>
* <p class="indent_big">get</p>
* <p class="indent_big">< attribute_list ></p>
* <p class="indent_big">< sequence sequence_request ></p>
* <p class="indent_big">< where filter_list ></p>
* <p class="indent_big">< limit integer ></p>
* <br>
* <p>- attribute_list is a comma-separated list of mart attributes. These must match the internal_name of attributes in the MartConfiguration for the mart being querried.
* attribute_list can be omitted for sequence_requests, otherwise, at least one attribute must be specified. Specifying attributes with
* a sequence_request requests that those attributes (if available) are included, either as fields in tabulated output, or in the description portion of the
* header in fasta output.</p>
* <p>- sequence_request follows the pattern:</p>
* <p class="indent_big"><left_flank_length_integer+>sequence_name<+right_flank_length_integer></p>
* <p class="indent_small">sequence_name must match the name of a sequence available from the system (see the SequenceDescription documentation below).
* <p class="indent_small">If specified with a left and/or right flank_length_integer (eg., 10+gene_exon, gene_exon+10, or 10+gene_exon+10)<p>
* <p class="indent_small">then the sequences will return the requested flanking sequence (although some sequences are not flankable).</p>
* <p>- dataset_name must match the internal_name of a dataset made available by the MartConfiguration made available for the mart being querried.</p>
* <p>- filter_list is a comma-separated list of filter_requests. filter_requests must match one of the following formats:</p>
* <p class="indent_big">- filter_name excluded|only -- specifies that objects should be returned only if they match (only),</p>
* <p class="indent_big">or only if they do not match (excluded) this filter.</p>
* <p class="indent_big">- filter_name =|!=|>|>=|<|<= value</p>
* <p class="indent_big">- filter_name in url -- specifies that the system should harvest the items in the specified url,</p>
* <p class="indent_big">and return objects only if they satisfy the condition of filter_name in (items in the url).</p>
* <p class="indent_big">The url must return a list of items, one per line.</p>
* <p class="indent_big">- filter_name in (comma-separated list of items) -- note the list must be enclosed in perentheses.</p>
* <p class="indent_big">- filter_name in (nested query) -- Nested query can be a MQL query, but it cannot include sequence_requests, or into requests.</p>
* <p class="indent_big">It also must only return one attribute, matching filter_name.</p>
* <p>filter_name must match the internal_name for a filter specified in the MartConfiguration for the mart being querried. Also, in some cases, the filter_name
* must be further qualified with a filter_set_name prepended with a period (eg, filter_set_name.filter_name = value). This is only the case when the filter
* is part of a filter_set, as specified in the MartConfiguration.</p>
* <br>
* <p>- if a limit request is specified, this adds a limit integer clause to the actual SQL executed against the mart database. Note that this does not necessarily
* limit the number of records returned to the specified integer. It limits the number of mart focus objects querried, for which attributes are returned.
* If there is a one-many, many-many, or many-one relationship between the mart focus object, and the attribute being requested, then the number of records returned
* will reflect this.</p>
* <br>
* <p>Note, the minimal MQL request would be 'get attribute_name'. The minimal sequence request would be 'get sequence sequence_name'.</p>
* <p>MQL differs from SQL in not requiring (or even allowing) multiple datasets, table ALLQUALIFIERS, or joins. You just have to specify the attributes/sequence that you want, the dataset to query, and filters to apply.</p>
* <p>This makes simple queries which are not that hard at the SQL level, even more simple.</p>
* <p>Because the Mart-Explorer engine resolves some filters to complex sub querries, it makes more complex underlying querries just as simple.</p>
* <p>Finally, it makes querries for things like sequences (which are impossible using SQL) just as simple.</p>
* <p>MQL statements can be written on one line, or separated with newlines and whitespace to make them easier to read</p>
* <br>
* <p>The default output settings are tab - separated, tabulated output
* to System.out. Client programs can over ride these settings using the appropriate setter methods
* but, once set, they remain in effect for the entire session, until another call to the
* setter(s) is(are) made).</p>
* @author <a href="mailto:dlondon@ebi.ac.uk">Darin London</a>
* @author <a href="mailto:craig@ebi.ac.uk">Craig Melsopp</a>
* @see org.ensembl.mart.lib.SequenceDescription
* @see org.ensembl.mart.lib.config.MartConfiguration
*/
public class MartShellLib {
/**
* Create a MartShellLib object with an empty adaptorManager,
* to be managed with add/remove/update commands.
*/
public MartShellLib() {
//dont ignoreCache, dont includeHiddenMembers
adaptorManager = new RegistryDSConfigAdaptor(false, true);
}
/**
* Create a MartShellLib object with a previously populated
* adaptorManager.
* @param adaptor RegistryDSConfigAdaptor object
*/
public MartShellLib(RegistryDSConfigAdaptor adaptor) {
adaptorManager = adaptor;
}
/**
* Add a MartRegistry to this MartShellLib instance. Optionally, instruct
* the system to bypass the caching system and fully load all DatasetConfig objects into
* memory (This option is not used by MartShell, but is available for users who wish
* to run this on a server with adequate memory).
* @param confFile - String path to a configuration file, either absolute, or relative to the ClASSPATH
* @param loadFully - boolean, if false, all DatasetConfig objects are cached to the filesystem. If true
* all DatasetConfig objects are fully loaded into memory, with no lazy loading.
* @throws ConfigurationException if confFile is not correctly parsed into a proper URL, and for all Configuration
* loading exceptions.
* @throws MalformedURLException if confFile is a malformed URL.
*/
public synchronized void addMartRegistry(String confFile, boolean loadFully) throws ConfigurationException, MalformedURLException {
URL confURL = InputSourceUtil.getURLForString(confFile);
if (confURL == null)
throw new ConfigurationException("Could not parse " + confFile + " into a URL\n");
//dont ignoreCache, dont includeHiddenMembers
RegistryDSConfigAdaptor adaptor = new RegistryDSConfigAdaptor(confURL, false, loadFully, true);
//see notes to adaptorManager for boolean settings
harvestAdaptorsFrom(adaptor);
}
/**
* Converts the Query into an MQL string.
*
* @param query Query object to be transformed into MQL
* @param datasetConfigquery Query object to be transformed into MQL
* @return String MQL statement
* throws InvalidQueryException for all underlying exceptions
*/
public String QueryToMQL(Query query, DatasetConfig datasetConfig) throws InvalidQueryException {
StringBuffer mqlbuf = new StringBuffer();
boolean success = getUsingClause(query, datasetConfig, mqlbuf);
if (success)
success = getGetClause(query, datasetConfig, mqlbuf.append(" "));
if (success && (query.getType() == Query.SEQUENCE))
getSequenceClause(query, mqlbuf.append(" "));
if (success && (query.getTotalFilterCount() > 0))
success = getWhereClause(query, datasetConfig, mqlbuf.append(" "));
if (success && query.hasLimit())
mqlbuf.append(" ").append("limit ").append(query.getLimit());
if (!success)
throw new InvalidQueryException("Could not compile MQL from Query\n" + MQLError + "\n");
return mqlbuf.toString();
}
/**
* Creates a Mart Query Language command from a Query object.
*
* @param query Query object to be transformed into MQL
* @return String MQL statement
* throws InvalidQueryException for all underlying exceptions
*/
public String QueryToMQL(Query query) throws InvalidQueryException, ConfigurationException {
//TODO: this is currently broken. Now need to store the DatasetConfig dataset and internalName in the Query for this to work
String datasetName = query.getDataset();
// get datasetName first
if (datasetName == null)
throw new InvalidQueryException("Recieved null DatasetName from query provided\n");
if (!adaptorManager.supportsDataset(datasetName))
throw new InvalidQueryException(
"DatasetConfig " + datasetName + " is not supported by the martConfiguration provided\n");
DatasetConfig dataset = adaptorManager.getDatasetConfigByDatasetInternalName(datasetName, null); //TODO:broken
return QueryToMQL(query, dataset);
}
private boolean getUsingClause(Query query, DatasetConfig dataset, StringBuffer mqlbuf) {
boolean success = true;
mqlbuf.append(USINGQSTART).append(" ").append(dataset.getInternalName());
return success;
}
private boolean getGetClause(Query query, DatasetConfig dataset, StringBuffer mqlbuf) {
Attribute[] attributes = query.getAttributes();
mqlbuf.append(GETQSTART);
if (attributes.length == 0) {
if (query.getType() == Query.SEQUENCE)
return true;
else {
MQLError = "Empty attributes, no Sequence.";
return false;
}
}
mqlbuf.append(" ");
boolean success = true;
for (int i = 0, n = attributes.length;(success && i < n); i++) {
if (i > 0)
mqlbuf.append(", ");
Attribute attribute = attributes[i];
String fname = attribute.getField();
String tconstraint = attribute.getTableConstraint();
if (dataset.supportsAttributeDescription(fname, tconstraint))
mqlbuf.append(dataset.getAttributeDescriptionByFieldNameTableConstraint(fname, tconstraint).getInternalName());
else {
success = false;
MQLError = "Could not map attribute " + attribute.getField() + " " + attribute.getTableConstraint();
}
}
return success;
}
private void getSequenceClause(Query query, StringBuffer mqlbuf) {
mqlbuf.append("sequence ");
SequenceDescription seqd = query.getSequenceDescription();
String seqtype = seqd.getSeqType();
int lflank = seqd.getLeftFlank();
if (lflank > 0)
mqlbuf.append(lflank).append("+");
mqlbuf.append(seqtype);
int rflank = seqd.getRightFlank();
if (rflank > 0)
mqlbuf.append("+").append(rflank);
}
private boolean getWhereClause(Query query, DatasetConfig datasetconfig, StringBuffer mqlbuf) {
boolean success = true;
mqlbuf.append("where ");
Filter[] filters = query.getFilters();
for (int i = 0, n = filters.length;(success && i < n); i++) {
if (i > 0)
mqlbuf.append(" and ");
Filter filter = filters[i];
if (filter instanceof BasicFilter)
success = mapBasicFilter((BasicFilter) filter, datasetconfig, mqlbuf);
else if (filter instanceof BooleanFilter)
success = mapBooleanFilter((BooleanFilter) filter, datasetconfig, mqlbuf);
else
success = mapIDListFilter((IDListFilter) filter, datasetconfig, mqlbuf);
if (!success)
MQLError = "Could not map filter " + filter.getField() + " " + filter.getTableConstraint();
}
return success;
}
private boolean mapBooleanFilter(BooleanFilter filter, DatasetConfig datasetconfig, StringBuffer mqlbuf) {
String field = filter.getField();
String tableConstraint = filter.getTableConstraint();
String filterCondition = filter.getQualifier();
if (!datasetconfig.supportsFilterDescription(field, tableConstraint, filterCondition))
return false;
FilterDescription fdesc =
datasetconfig.getFilterDescriptionByFieldNameTableConstraint(field, tableConstraint, filterCondition);
String filterName = fdesc.getInternalNameByFieldNameTableConstraint(field, tableConstraint, filterCondition);
mqlbuf.append(filterName);
if (filterCondition.equals(BooleanFilter.isNULL) || filterCondition.equals(BooleanFilter.isNotNULL_NUM))
mqlbuf.append(" excluded");
else
mqlbuf.append(" only");
return true;
}
private boolean mapIDListFilter(IDListFilter filter, DatasetConfig datasetconfig, StringBuffer mqlbuf) {
String field = filter.getField();
String tableConstraint = filter.getTableConstraint();
String filterCondition = filter.getQualifier();
if (!datasetconfig.supportsFilterDescription(field, tableConstraint, filterCondition))
return false;
boolean success = true;
FilterDescription fdesc =
datasetconfig.getFilterDescriptionByFieldNameTableConstraint(field, tableConstraint, filterCondition);
String filterName = fdesc.getInternalNameByFieldNameTableConstraint(field, tableConstraint, filterCondition);
mqlbuf.append(filterName).append(" in ");
//String handler = filter.getHandler();
String handler = null;
if (handler.equals(IDListFilter.FILE)) {
mqlbuf.append(filter.getFile());
} else if (handler.equals(IDListFilter.URL)) {
mqlbuf.append(filter.getUrl());
} else if (handler.equals(IDListFilter.SUBQUERY)) {
Query subq = filter.getSubQuery();
mqlbuf.append(subq.getQueryName());
try {
mqlbuf.insert(0, QueryToMQL(subq) + " as " + subq.getQueryName() + ";");
} catch (Exception e) {
success = false;
MQLError = ("Could not map subquery:\n" + subq + "\n" + e);
}
} else {
String[] ids = filter.getIdentifiers();
mqlbuf.append("(");
for (int i = 0, n = ids.length; i < n; i++) {
if (i > 0)
mqlbuf.append(", ");
mqlbuf.append(ids[i]);
}
mqlbuf.append(")");
}
return success;
}
private boolean mapBasicFilter(BasicFilter filter, DatasetConfig datasetconfig, StringBuffer mqlbuf) {
String field = filter.getField();
String tableConstraint = filter.getTableConstraint();
String filterCondition = filter.getQualifier();
if (!datasetconfig.supportsFilterDescription(field, tableConstraint, filterCondition))
return false;
FilterDescription fdesc =
datasetconfig.getFilterDescriptionByFieldNameTableConstraint(field, tableConstraint, filterCondition);
mqlbuf
.append(fdesc.getInternalNameByFieldNameTableConstraint(field, tableConstraint, filterCondition))
.append(" ")
.append(filter.getQualifier())
.append(" ")
.append(filter.getValue());
return true;
}
public void setMaxCharCount(int max) {
maxcharcount = max;
}
/**
* Allows users to store MQL to use as subqueries in other MQL.
*
* @param key - String name to refer to this stored Command in later queries.
* @param mql - String mql subquery.
*/
public synchronized void addStoredMQLCommand(String key, String mql) {
storedCommands.put(key, mql);
}
/**
* Remove a stored MQL statement with its key.
* @param key -- key for stored MQL command
*/
public synchronized void removeStoredMQLCommand(String key) {
if (storedCommands.containsKey(key)) {
storedCommands.remove(key);
}
}
/**
* Returns the actual MQL statement stored for a particular key, or null if not stored
* @param key -- key for a stored MQL command
* @return String MQL command
*/
public synchronized String describeStoredMQLCommand(String key) {
return storedCommands.getProperty(key);
}
/**
* Returns a query object for a stored procedure. This only works for
* queries without bind variables.
* @param key -- key for a MQL command
* @return Query parsed from stored MQL command
* @throws InvalidQueryException for any query parsing Exceptions
*/
public synchronized Query StoredMQLCommandToQuery(String key) throws InvalidQueryException {
return MQLtoQuery(describeStoredMQLCommand(key));
}
/**
* Get a Set containing all stored MQL Procedure keys.
* @return Set
*/
public synchronized Set getStoredMQLCommandKeys() {
return storedCommands.keySet();
}
public String[] listDatasets(String[] toks) throws ConfigurationException {
if (adaptorManager.getDatasetNames(false).length == 0)
return new String[] { "No Datasets Loaded\n" };
List retList = new ArrayList();
if (toks.length == 3) {
//list datasets all|sourceName
String reqName = toks[2];
if (reqName.equalsIgnoreCase(LISTALLREQ)) {
//list datasets all
String[] sources = adaptorManager.getAdaptorNames();
for (int i = 0, n = sources.length; i < n; i++) {
String source = sources[i];
DSConfigAdaptor adaptor = adaptorManager.getAdaptorByName(source);
String[] datasets = adaptor.getDatasetNames(false);
for (int j = 0, m = datasets.length; j < m; j++) {
retList.add( /*canonicalizeMartName( source )*/ source + "." + datasets[j] + "\n"); // CHANGED
}
}
} else {
//list datasets sourceName
if (!adaptorManager.supportsAdaptor( reqName /*deCanonicalizeMartName( reqName )*/ )) // CHANGED
throw new ConfigurationException(reqName + " is not a valid Mart Source to list Datasets\n");
String[] datasets = adaptorManager.getAdaptorByName( reqName /*deCanonicalizeMartName( reqName )*/ ).getDatasetNames(false); //CHANGED
for (int i = 0, n = datasets.length; i < n; i++) {
retList.add(reqName + "." + datasets[i] + "\n");
}
}
} else if (toks.length == 2) {
//list datasets (relative to envMart)
if (envMart == null)
throw new ConfigurationException("Must set environmental Mart to list Datasets relative to it\n");
String reqName = envMart.getName();
String[] datasets = adaptorManager.getAdaptorByName(reqName).getDatasetNames(false);
for (int i = 0, n = datasets.length; i < n; i++) {
retList.add(datasets[i] + "\n");
}
} else
throw new ConfigurationException("Invalid List Datasets Request\n");
String[] ret = new String[retList.size()];
retList.toArray(ret);
Arrays.sort(ret);
return ret;
}
public String[] listDatasetConfigs(String[] toks) throws ConfigurationException {
if (adaptorManager.getNumDatasetConfigs(true) == 0)
return new String[] { "No DatasetConfigs Loaded\n" };
List retList = new ArrayList();
if (toks.length == 3) {
//list datasetconfigs all|sourcename
String reqName = toks[2];
if (reqName.equalsIgnoreCase(LISTALLREQ)) {
//list datasetconfigs all
String[] sources = adaptorManager.getAdaptorNames();
for (int i = 0, n = sources.length; i < n; i++) {
String source = sources[i];
DSConfigAdaptor adaptor = adaptorManager.getAdaptorByName(source);
String[] datasets = adaptor.getDatasetNames(false);
for (int j = 0, m = datasets.length; j < m; j++) {
String dataset = datasets[j];
String[] configs = adaptor.getDatasetConfigInternalNamesByDataset(dataset);
for (int k = 0, l = configs.length; k < l; k++) {
String config = configs[k];
retList.add( source /*canonicalizeMartName( source )*/ + "." + dataset + "." + config + "\n"); // CHANGED
}
}
}
} else {
//list datasetconfigs sourcename
if (!adaptorManager.supportsAdaptor( reqName /*deCanonicalizeMartName( reqName )*/ )) // CHANGED
throw new ConfigurationException("Source " + reqName + " is not a valid Mart Source\n");
DSConfigAdaptor adaptor = adaptorManager.getAdaptorByName(reqName /*deCanonicalizeMartName( reqName )*/ ); // CHANGED
String[] datasets = adaptor.getDatasetNames(false);
for (int j = 0, m = datasets.length; j < m; j++) {
String[] configs = adaptor.getDatasetConfigInternalNamesByDataset(datasets[j]);
for (int k = 0, l = configs.length; k < l; k++) {
retList.add(reqName + "." + datasets[j] + "." + configs[k] + "\n");
}
}
}
} else if (toks.length == 2) {
//list datasetconfigs (relative to envMart and envDataset
if (envMart == null)
throw new ConfigurationException("Must set environmental Mart to list DatasetConfigs to it\n");
if (envDataset == null)
throw new ConfigurationException("Must set environmental Dataset to list DatasetConfigs to it\n");
String[] configs =
adaptorManager.getAdaptorByName(envMart.getName()).getDatasetConfigInternalNamesByDataset(
envDataset.getInternalName());
for (int i = 0, n = configs.length; i < n; i++) {
retList.add(configs[i] + "\n");
}
} else
throw new ConfigurationException("Invalid list datasetconfigs command recieved\n");
String[] ret = new String[retList.size()];
retList.toArray(ret);
Arrays.sort(ret);
return ret;
}
public String[] listProcedures() {
if (getStoredMQLCommandKeys().size() == 0)
return new String[] { "No Procedures Stored\n" };
Set names = getStoredMQLCommandKeys();
String[] ret = new String[names.size()];
int i = 0;
for (Iterator iter = names.iterator(); iter.hasNext();) {
String name = (String) iter.next();
ret[i] = name + "\n";
i++;
}
Arrays.sort(ret);
return ret;
}
public String[] listMarts() throws ConfigurationException {
if (adaptorManager.getAdaptorNames().length == 0)
throw new ConfigurationException("No Marts have been loaded\n");
String[] names = adaptorManager.getAdaptorNames();
ArrayList martNames = new ArrayList();
for (int i = 0, n = names.length; i < n; i++) {
if (adaptorManager.getAdaptorByName( names[i] ).getNumDatasetConfigs(true) > 0)
martNames.add( names[i] /*canonicalizeMartName( names[i] )*/ + "\n" ); // CHANGED
}
String[] ret = new String[martNames.size()];
martNames.toArray(ret);
Arrays.sort(ret);
return ret;
}
public String[] listFilters() throws InvalidQueryException, ConfigurationException {
if (envDataset == null)
throw new InvalidQueryException("Must set the environmental Dataset to list filters\n");
int blen = 3; //3 filters/line
List columns = new ArrayList();
String[] buffer = new String[blen];
int[] maxlengths = new int[] { 0, 0, 0 };
List names = envDataset.getFilterCompleterNames();
Collections.sort(names);
int pos = 0;
for (Iterator iter = names.iterator(); iter.hasNext();) {
String name = (String) iter.next();
if (pos == blen) {
columns.add(buffer);
buffer = new String[blen];
pos = 0;
}
buffer[pos] = name;
if (name.length() > maxlengths[pos])
maxlengths[pos] = name.length();
pos++;
}
if (pos > 0)
columns.add(buffer);
return formatColumns(columns, maxlengths);
}
public String[] listAttributes() throws ConfigurationException, InvalidQueryException {
if (envDataset == null)
throw new InvalidQueryException("Must set the environmental Dataset to list attributes\n");
int blen = 3; //3 atts/line
List columns = new ArrayList();
String[] buffer = new String[blen];
int[] maxlengths = new int[] { 0, 0, 0 };
List names = envDataset.getAttributeCompleterNames();
Collections.sort(names);
int pos = 0;
for (Iterator iter = names.iterator(); iter.hasNext();) {
String name = (String) iter.next();
if (pos == blen) {
columns.add(buffer);
buffer = new String[blen];
pos = 0;
}
buffer[pos] = name;
if (name.length() > maxlengths[pos])
maxlengths[pos] = name.length();
pos++;
}
if (pos > 0)
columns.add(buffer);
return formatColumns(columns, maxlengths);
}
private String[] formatColumns(List columns, int[] maxlengths) {
int[] pos = new int[] { 0, 0, 0 }; // position matrix, change pos[0] to increase leftmost padding
int maxtotal = 0;
for (int i = 0, n = maxlengths.length; i < n; i++) {
maxtotal += maxlengths[i];
}
int minSpace = 5; //default
if (maxtotal < maxcharcount)
minSpace = (maxcharcount - maxtotal) / (maxlengths.length - 1);
//calculate positions 2 onward
for (int i = 1, n = maxlengths.length; i < n; i++) {
pos[i] = pos[i - 1] + maxlengths[i - 1] + minSpace;
}
List lines = new ArrayList();
for (Iterator iter = columns.iterator(); iter.hasNext();) {
String[] lc = (String[]) iter.next();
StringBuffer thisLine = new StringBuffer();
int len = thisLine.length();
for (int i = 0, n = lc.length; i < n; i++) {
if (lc[i] != null) {
while (len < pos[i]) {
thisLine.append(" ");
len++;
}
thisLine.append(lc[i]);
len = thisLine.length();
}
}
thisLine.append("\n");
lines.add(thisLine.toString());
}
String[] ret = new String[lines.size()];
lines.toArray(ret);
return ret;
}
public String DescribeMart(String name) throws InvalidQueryException {
DetailedDataSource reqMart = null;
try {
if (name == null) {
if (envMart == null)
throw new InvalidQueryException("Invalid describe Mart command recieved, must set environmental Mart with 'set' or 'use', or explicitly supply martName to describe\n");
reqMart = envMart;
} else {
if (!adaptorManager.supportsAdaptor( name /*deCanonicalizeMartName( name )*/ ) ) // CHANGED
throw new InvalidQueryException(MARTREQ + " " + name + " has not been stored\n");
reqMart = adaptorManager.getAdaptorByName(name /* deCanonicalizeMartName( name ) */).getDataSource(); // CHANGED
//this could (but probably wont) be a file sourcename
if (reqMart == null)
throw new InvalidQueryException("Source " + name + "is a file Source\n");
}
} catch (InvalidQueryException e) {
throw e;
} catch (ConfigurationException e) {
throw new InvalidQueryException("Caught ConfigurationException describing Mart " + name + "\n");
}
String ret =
"Mart: "
+ name
+ " HOST: "
+ reqMart.getHost()
+ " USER: "
+ reqMart.getUser()
+ " MART NAME: "
+ reqMart.getDatabaseName();
return ret;
}
public String[] DescribeDataset(String dsetname) throws ConfigurationException, InvalidQueryException {
DatasetConfig dset = null;
if (dsetname == null) {
if (envDataset == null)
throw new InvalidQueryException("Invalid describe dataset command, please set the environmental Dataset and Mart with either 'use' or 'set'\n");
dset = envDataset;
} else {
//TODO: getLocalConfigFor(dsetname); dset = localDataset (reference, so no further datasets pulled into memory)
dset = getDatasetConfigFor(dsetname);
}
List lines = new ArrayList();
//filters first
FilterPage[] fpages = dset.getFilterPages();
for (int i = 0, n = fpages.length; i < n; i++) {
FilterPage page = fpages[i];
lines.add("The following filters can be applied in the same query\n");
lines.add("\n");
List names = page.getCompleterNames();
for (int j = 0, m = names.size(); j < m; j++) {
String name = (String) names.get(j);
lines.add(DescribeFilter(page.getFilterDescriptionByInternalName(name)));
lines.add("\n");
}
}
//attributes
AttributePage[] apages = dset.getAttributePages();
for (int i = 0, n = apages.length; i < n; i++) {
AttributePage page = apages[i];
lines.add("\n");
lines.add("\n");
lines.add("The following Attributes can be querried together\n");
lines.add(
"numbers in perentheses denote groups of attributes that have limits on the number that can be queried together\n");
lines.add("\n");
List groups = page.getAttributeGroups();
for (Iterator iter = groups.iterator(); iter.hasNext();) {
Object obj = iter.next();
if (obj instanceof AttributeGroup) {
AttributeGroup group = (AttributeGroup) obj;
AttributeCollection[] cols = group.getAttributeCollections();
for (int j = 0, m = cols.length; j < m; j++) {
AttributeCollection collection = cols[j];
List atts = collection.getAttributeDescriptions();
int maxSelect = collection.getMaxSelect();
for (Iterator iterator = atts.iterator(); iterator.hasNext();) {
Object element = iterator.next();
String tmp = DescribeAttribute(element);
if (maxSelect > 0)
lines.add(tmp + " (" + maxSelect + ")");
else
lines.add(tmp);
lines.add("\n");
}
}
}
}
}
String[] ret = new String[lines.size()];
lines.toArray(ret);
dset = null; //for gc
return ret;
}
public String DescribeFilter(String name) throws InvalidQueryException {
if (envMart == null)
throw new InvalidQueryException("Must set environmental Mart with a 'use' or 'set' command for describe filter to work\n");
if (envDataset == null)
throw new InvalidQueryException("Must set environmental Dataset with a 'use' or 'set' command for describe filter to work\n");
if (!(envDataset.containsFilterDescription(name)))
throw new InvalidQueryException(
"Filter " + name + " is not supported by Environmental Dataset " + envDataset.getDataset() + "\n");
return DescribeFilter(envDataset.getFilterDescriptionByInternalName(name));
}
private String DescribeFilter(FilterDescription desc) throws InvalidQueryException {
String name = desc.getInternalName();
List quals = desc.getCompleterQualifiers(name);
StringBuffer qual = new StringBuffer();
for (int k = 0, l = quals.size(); k < l; k++) {
if (k > 0)
qual.append(", ");
String element = (String) quals.get(k);
qual.append(element);
}
String qualifiers = qual.toString();
String displayName = desc.getDisplayname(name);
return name + " - " + displayName + " (" + qualifiers + ")";
}
public String DescribeAttribute(String name) throws InvalidQueryException {
if (envDataset == null)
throw new InvalidQueryException("Must set environmental Dataset with a 'use' or 'set' command for describe attribute to work\n");
if (envMart == null)
throw new InvalidQueryException("Must set environmental Mart with a 'use' or 'set' command for describe attribute to work\n");
if (!envDataset.containsAttributeDescription(name))
throw new InvalidQueryException(
"Attribute " + name + " is not supported by environmental Dataset " + envDataset.getInternalName() + "\n");
String tmp = DescribeAttribute(envDataset.getAttributeDescriptionByInternalName(name));
return tmp;
}
private String DescribeAttribute(Object attributeo) {
if (attributeo instanceof AttributeDescription) {
AttributeDescription desc = (AttributeDescription) attributeo;
String iname = desc.getInternalName();
String displayName = desc.getDisplayName();
return iname + " - " + displayName;
} else
//dsattributedescription, if ever implimented
return null;
}
/**
* Add a Mart to MartShellLib. Interface must collect all necessary connection paramaters.
* @param martDatabaseType
* @param martHost
* @param martPort
* @param martDatabase
* @param martUser
* @param martPass
* @param martDriver
* @param sourceKey - name to reference Mart in queries
* @return boolean, true if the Database settings resulted in a successful connection, false otherwise
* @throws InvalidQueryException
* @see org.ensembl.mart.lib.DetailedDataSource for further information about parameter meanings
*/
public void addMart(
String martDatabaseType,
String martHost,
String martPort,
String martDatabase,
String martSchema,
String martUser,
String martPass,
String martDriver,
String sourceKey)
throws InvalidQueryException {
DetailedDataSource ds =
new DetailedDataSource(
martDatabaseType,
martHost,
martPort,
martDatabase,
martSchema,
martUser,
martPass,
DetailedDataSource.DEFAULTPOOLSIZE,
martDriver,
sourceKey);
Connection conn = null;
try {
conn = ds.getConnection();
addMart(ds);
} catch (SQLException e) {
throw new InvalidQueryException("Could not connect to Database with given settings, please try again\n", e);
} finally {
DetailedDataSource.close(conn);
}
}
public void addMart(DetailedDataSource ds) throws InvalidQueryException {
try {
DatabaseDSConfigAdaptor adaptor = new DatabaseDSConfigAdaptor(ds, ds.getUser(), ds.getMartUser(), false, false, true, true);
//see notes for adaptorManager for boolean settings
adaptor.setName(ds.getName());
adaptorManager.add(adaptor);
} catch (ConfigurationException e) {
throw new InvalidQueryException("Problem creating Mart " + e.getMessage(), e);
}
//for convenience, set envMart to the latest added Mart
envMart = ds;
}
public void addDatasets(StringTokenizer toks) throws InvalidQueryException {
if (toks.countTokens() == 2) {
toks.nextToken(); // ignore from
String source = toks.nextToken();
try {
URL regURL = InputSourceUtil.getURLForString(source);
RegistryDSConfigAdaptor regadaptor = new RegistryDSConfigAdaptor(regURL, false, false, true);
// see notes for adaptorManager for boolean settings
harvestAdaptorsFrom(regadaptor);
} catch (MalformedURLException e) {
throw new InvalidQueryException(
"Recieved MalformedURLException parsing " + source + " into a URL " + e.getMessage() + "\n",
e);
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Recieved ConfigurationException loading DatasetConfigs from " + source + " " + e.getMessage() + "\n",
e);
}
} else
throw new InvalidQueryException("Recieved invalid add DatasetConfigs command.\n");
}
//recursively harvest DB and URL adaptors from composite/registry adaptors
public void harvestAdaptorsFrom(DSConfigAdaptor adaptor) throws ConfigurationException {
if (adaptor instanceof CompositeDSConfigAdaptor) {
DSConfigAdaptor[] adaptors = adaptor.getLeafAdaptors();
//returns all leaf adaptors (DatabaseDSConfigAdaptor, URLDSConfigAdaptor) from a CompositeDSViewAdaptor
for (int i = 0, n = adaptors.length; i < n; i++)
addLeafAdaptor(adaptors[i]);
} else
addLeafAdaptor(adaptor);
}
private void addLeafAdaptor(DSConfigAdaptor adaptor) throws ConfigurationException {
if (adaptor instanceof DatabaseDSConfigAdaptor) {
//test the connection
try {
((DSConfigAdaptor) adaptor).getDataSource().getConnection();
adaptorManager.add(adaptor);
} catch (SQLException e) {
//ignore this one, it cant connect to its underlying database
}
} else
adaptorManager.add(adaptor);
}
public void addDatasetConfig(StringTokenizer toks) throws InvalidQueryException {
if (toks.hasMoreTokens()) {
String source = toks.nextToken();
String userName = null;
if (toks.hasMoreTokens()) {
if (toks.nextToken().equals("as"))
userName = toks.nextToken();
}
try {
URL dsvURL = InputSourceUtil.getURLForString(source);
URLDSConfigAdaptor adaptor = new URLDSConfigAdaptor(dsvURL, false, true);
// see notes for adaptorManager for boolean settings
//dont ignoreCache, dont includeHiddenMembers
if (userName == null) {
CompositeDSConfigAdaptor fileAdaptor = null;
if (adaptorManager.supportsAdaptor(DEFAULTURLADAPTORNAME)) {
fileAdaptor = (CompositeDSConfigAdaptor) adaptorManager.getAdaptorByName(DEFAULTURLADAPTORNAME);
adaptorManager.remove(fileAdaptor);
} else {
fileAdaptor = new CompositeDSConfigAdaptor();
fileAdaptor.setName(DEFAULTURLADAPTORNAME);
}
fileAdaptor.add(adaptor);
adaptorManager.add(fileAdaptor);
} else {
adaptor.setName(userName);
adaptorManager.add(adaptor);
}
} catch (MalformedURLException e) {
throw new InvalidQueryException(
"Recieved MalformedURLException parsing " + source + " into a URL " + e.getMessage() + "\n",
e);
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Recieved ConfigurationException loading DatasetConfig from " + source + " " + e.getMessage() + "\n",
e);
}
} else
throw new InvalidQueryException("Recieved invalid add DatasetConfig command.\n");
}
public void removeProcedure(StringTokenizer toks) throws InvalidQueryException {
if (toks.hasMoreTokens()) {
String name = toks.nextToken();
removeStoredMQLCommand(name);
} else
throw new InvalidQueryException("Recieved invalid remove Procedure command.\n");
}
public void removeMart(StringTokenizer toks) throws InvalidQueryException {
if (toks.hasMoreTokens()) {
String name = toks.nextToken();
try {
//If a DSConfigAdaptor has been created with this Mart, remove it
if (adaptorManager.supportsAdaptor(name))
adaptorManager.remove(adaptorManager.getAdaptorByName(name));
else
throw new InvalidQueryException("Unknown Mart " + name + "\n");
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Caught ConfigurationException removing adaptor for Mart " + name + "\n" + e.getMessage(),
e);
} catch (InvalidQueryException e) {
throw e;
}
} else
throw new InvalidQueryException("Recieved invalid remove Mart command.\n");
}
public void removeDataset(StringTokenizer toks) throws InvalidQueryException {
if (toks.hasMoreTokens()) {
String name = toks.nextToken();
//special case where x.y must be of sourcename.datasetname
String[] nametoks = name.split("\\.");
try {
if (nametoks.length == 2) {
if (!(adaptorManager.supportsAdaptor(nametoks[0]) && adaptorManager.supportsDataset(nametoks[1])))
throw new InvalidQueryException(
"Cannot remove dataset with name: "
+ name
+ " must remove it with datasetname relative to the environmental mart, or sourcename.datasetname explicitly\n");
DSConfigAdaptor adaptor = adaptorManager.getAdaptorByName(nametoks[0]);
if (adaptor == null)
throw new InvalidQueryException("Nothing loaded for Mart " + nametoks[0] + "\n");
if (adaptor.supportsDataset(nametoks[1])) {
if (adaptor instanceof MultiDSConfigAdaptor) {
MultiDSConfigAdaptor dbadaptor = (MultiDSConfigAdaptor) adaptor;
DatasetConfigIterator dsvs = dbadaptor.getDatasetConfigsByDataset(nametoks[1]);
while (dsvs.hasNext())
dbadaptor.removeDatasetConfig((DatasetConfig) dsvs.next());
if (dbadaptor.getNumDatasetConfigs(true) < 1)
adaptorManager.remove(dbadaptor);
} else
adaptorManager.remove(adaptor);
} else
throw new InvalidQueryException("Mart " + nametoks[0] + " does not support dataset " + nametoks[1] + "\n");
} else if (nametoks.length == 1) {
if (envMart == null)
throw new InvalidQueryException("Must set environmental Mart to remove datasets relative to it.");
DSConfigAdaptor adaptor = adaptorManager.getAdaptorByName(envMart.getName());
if (adaptor.supportsDataset(nametoks[0])) {
if (adaptor instanceof MultiDSConfigAdaptor) {
MultiDSConfigAdaptor dbadaptor = (MultiDSConfigAdaptor) adaptor;
DatasetConfigIterator dsvs = dbadaptor.getDatasetConfigsByDataset(nametoks[1]);
while (dsvs.hasNext()) {
dbadaptor.removeDatasetConfig((DatasetConfig) dsvs.next());
}
if (dbadaptor.getNumDatasetConfigs(true) < 1)
adaptorManager.remove(dbadaptor);
} else
adaptorManager.remove(adaptor);
} else
throw new InvalidQueryException(
"Dataset " + nametoks[0] + " is not supported by environmental Mart " + envMart.getName() + "\n");
} else
throw new InvalidQueryException(
"Cannot remove dataset with name "
+ name
+ " must remove it with datasetname relative to the environmental mart, or sourcename.datasetname explicitly\n");
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Caught ConfigurationException removing dataset " + name + " " + e.getMessage(),
e);
} catch (InvalidQueryException e) {
throw e;
}
} else
throw new InvalidQueryException("Invalid remove dataset command.\n");
}
public void removeDatasets(StringTokenizer toks) throws InvalidQueryException {
if (toks.countTokens() == 2) {
toks.nextToken(); // skip from
String source = toks.nextToken();
try {
if (adaptorManager.supportsAdaptor(source))
adaptorManager.remove(adaptorManager.getAdaptorByName(source));
else
throw new InvalidQueryException("Source " + source + " has not been loaded.\n");
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Caught ConfigurationException removing DatasetConfigs from source " + source + "\n" + e.getMessage(),
e);
}
} else if (toks.countTokens() == 1) {
if (envMart == null)
throw new InvalidQueryException("Environmental Mart not set, no datasets to remove\n");
try {
if (adaptorManager.supportsAdaptor(envMart.getName()))
adaptorManager.remove(adaptorManager.getAdaptorByName(envMart.getName()));
else
throw new InvalidQueryException("envMart: " + envMart.getName() + " does not have any datasets loaded\n");
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Caught ConfigurationException removing envMart " + envMart.getName() + " Datasets\n" + e.getMessage(),
e);
} catch (InvalidQueryException e) {
throw e;
}
} else
throw new InvalidQueryException("Recieved invalid remove DatasetConfigs comand.\n");
}
public void removeDatasetConfig(StringTokenizer toks) throws InvalidQueryException {
if (toks.hasMoreTokens()) {
String name = toks.nextToken();
try {
//special case where it does not allow remove dataset sourcename.datasetname (eg. must explicitly use sourcename.datasetname.default to remove default)
String[] nametoks = name.split("\\.");
if (nametoks.length == 2
&& adaptorManager.supportsAdaptor( nametoks[0] /*deCanonicalizeMartName( nametoks[0] )*/) // CHANGED
&& adaptorManager.supportsDataset(nametoks[1]))
throw new InvalidQueryException(
"Cannot remove default datasetconfig for dataset with relative name: "
+ name
+ " must remove it with sourcename.datasetname.configname explicitly\n");
adaptorManager.removeDatasetConfig(getDatasetConfigFor(name));
} catch (ConfigurationException e) {
throw new InvalidQueryException(
"Caught ConfigurationException removing DatasetConfig " + name + " " + e.getMessage(),
e);
} catch (InvalidQueryException e) {
throw e;
}
} else
throw new InvalidQueryException("Recieved invalid remove DatasetConfig command.\n");
}
public void updateDatasets(StringTokenizer toks) throws InvalidQueryException {
try {
if (toks.hasMoreTokens()) {
toks.nextToken(); // skip from
String source = toks.nextToken();
if (adaptorManager.supportsAdaptor( source /*deCanonicalizeMartName( source )*/ )) // CHANGED
adaptorManager.getAdaptorByName( source /*deCanonicalizeMartName( source )*/ ).update(); // CHANGED
else
throw new InvalidQueryException("Invalid Mart Source " + source + " passed in update datasets request\n");
} else {
if (envMart == null)
throw new InvalidQueryException("Environment Mart not set, cannot update datasets\n");
adaptorManager.getAdaptorByName(envMart.getName()).update();
}
} catch (ConfigurationException e) {
throw new InvalidQueryException("Could not update DatasetConfigs, " + e.getMessage() + "\n", e);
}
}
public void updateDataset(StringTokenizer toks) throws InvalidQueryException {
if (toks.hasMoreTokens()) {
String name = toks.nextToken();
try {
String[] nametoks = name.split("\\.");
if (nametoks.length == 2) {
//sourcename.datasetname
if (!adaptorManager.supportsAdaptor(nametoks[0] /*deCanonicalizeMartName( nametoks[0] )*/ )) // CHANGED
throw new InvalidQueryException("Source " + nametoks[0] + " is not a valid mart source\n");
DSConfigAdaptor adaptor = adaptorManager.getAdaptorByName( nametoks[0] /*deCanonicalizeMartName( nametoks[0] )*/ ); // CHANGED
if (!adaptor.supportsDataset(nametoks[1]))
throw new InvalidQueryException(
"Dataset " + nametoks[1] + " is not supported by Mart Source " + nametoks[0] + "\n");
adaptor.update();
} else if (nametoks.length == 1) {
//datasetname relative to environmental Mart
if (envMart == null)
throw new InvalidQueryException(
"Must set environmental Mart to update datasetconfigs with relative name " + nametoks[0] + "\n");
if (!adaptorManager.getAdaptorByName(envMart.getName()).supportsDataset(nametoks[0]))
throw new InvalidQueryException(
"Mart " + envMart.getName() + " does not support dataset " + nametoks[0] + "\n");
adaptorManager.getAdaptorByName(envMart.getName()).update();
} else
throw new InvalidQueryException(
"Recieved invalid update DatasetConfig command update datasetconfig " + name + "\n");
} catch (ConfigurationException e) {
throw new InvalidQueryException("Could not update DatasetConfig " + name + " " + e.getMessage(), e);
}
} else
throw new InvalidQueryException("Recieved invalid update DatasetConfig command.\n");
}
public DatasetConfig getDatasetConfigFor(String name) throws InvalidQueryException {
DatasetConfig ret = null;
DatasetRequest dsetreq = new DatasetRequest(name, this);
try {
ret =
adaptorManager.getAdaptorByName(dsetreq.mart).getDatasetConfigByDatasetInternalName(
dsetreq.dataset,
dsetreq.datasetconfig);
} catch (ConfigurationException e) {
throw new InvalidQueryException("Could not parse " + name + " for DatasetConfig : " + e.getMessage() + "\n");
}
if (ret == null)
throw new InvalidQueryException("Could not manipulate DatasetConfig " + name + "\n");
return ret;
}
/**
* Allows Completer to set the localDataset and usingLocalDataset values with a string to be parsed.
* Does not set localDatasetConfig if it is not changed.
* @param name -- name to be parsed into a DatasetConfig, either with absolute path, or relative to envMart or envDataset.
*/
protected void setLocalDatasetFor(String name) throws InvalidQueryException {
DatasetConfig dset = getDatasetConfigFor(name);
if (localDataset == null || !localDataset.equals(dset))
localDataset = dset;
}
public void setEnvMart(String name) throws InvalidQueryException {
if (name == null) {
//unset command
envMart = null;
} else {
try {
if (!adaptorManager.supportsAdaptor( name /*deCanonicalizeMartName( name )*/)) //CHANGED
throw new InvalidQueryException("Invalid Mart name " + name + " recieved in set Mart command\n");
DetailedDataSource ds = adaptorManager.getAdaptorByName( name /*deCanonicalizeMartName( name )*/ ).getDataSource(); //CHANGED
if (ds == null)
throw new InvalidQueryException("Source " + name + " is a File Source\n");
envMart = ds;
} catch (ConfigurationException e) {
throw new InvalidQueryException("Caught ConfigurationException setting Mart to " + name + "\n");
} catch (InvalidQueryException e) {
throw e;
}
}
}
public void setEnvDataset(String command) throws InvalidQueryException {
if (command == null) {
//unset command
envDataset = null;
} else {
DatasetRequest dsrq = new DatasetRequest(command, this);
try {
envDataset = adaptorManager.getAdaptorByName(dsrq.mart).getDatasetConfigByDatasetInternalName(dsrq.dataset, dsrq.datasetconfig);
if (envMart == null || !(envMart.getName().equals(dsrq.mart)))
envMart = adaptorManager.getAdaptorByName(dsrq.mart).getDataSource();
} catch (ConfigurationException e) {
throw new InvalidQueryException("Could not parse set Dataset command " + command + ": " + e.getMessage() + "\n");
}
}
}
public String showEnvMart() {
if (envMart == null)
return " Mart not set\n";
else
return " Mart HOST: "
+ envMart.getHost()
+ " USER: "
+ envMart.getUser()
+ " MART NAME: "
+ envMart.getDatabaseName()
+ "\n";
}
public String showEnvDataset() {
if (envDataset == null)
return " Environmental DataSet not set\n";
else {
//determine if it is a URLDSConfigAdaptor DatasetConfig
DSConfigAdaptor adaptor = envDataset.getAdaptor();
if (adaptor != null && adaptor instanceof URLDSConfigAdaptor)
return adaptor.getName() + "." + envDataset.getDataset() + "\n";
else
return " Dataset " + envDataset.getDataset() + "\n";
}
}
public String showEnvDataSetConfig() {
if (envDataset == null)
return " Environmental DataSetConfig not set\n";
else
return " DatasetConfig " + envDataset.getDataset() + "." + envDataset.getInternalName() + "\n";
}
/**
* Creates a Query object from a Mart Query Language command.
*
* @param mql - String MQL command to parse into a Query object
* @return Query object
* @throws InvalidQueryException for all underlying exceptions (MQL syntax errors, DatasetConfig/Attributes/Sequences/Filters not found, etc.)
*/
public synchronized Query MQLtoQuery(String newquery) throws InvalidQueryException {
try {
//reset MartCompleter induced state, if any. Also reduces the number of DatasetConfig objects held in memory
usingLocalDataset = false;
localDataset = null;
boolean start = true;
boolean getClause = false;
boolean usingClause = false;
boolean domainSpecificClause = false;
boolean whereClause = false;
boolean limitClause = false;
boolean sortClause = false;
boolean inList = false;
boolean inBind = false;
boolean inQuotedValue = false;
boolean whereFilterName = false;
boolean whereFilterCond = false;
boolean whereFilterVal = false;
boolean validQuery = false;
DatasetConfig thisDatasetConfig = null;
if (logger.isLoggable(Level.FINE))
logger.fine("Recieved Query " + newquery + "\n");
Query query = new Query();
currentFpage = null;
currentApage = null;
atts = new ArrayList();
filtNames = new ArrayList();
maxSelects = new Hashtable();
String filterName = null;
String filterCondition = null;
StringBuffer filterValue = new StringBuffer();
StringBuffer storedCommand = new StringBuffer();
List listFilterValues = new ArrayList();
String domainSpecificKeyword = null;
StringTokenizer cTokens = new StringTokenizer(newquery, " ");
if (cTokens.countTokens() < 2)
throw new InvalidQueryException("\nInvalid Query Recieved " + newquery + "\n");
while (cTokens.hasMoreTokens()) {
String thisToken = cTokens.nextToken();
if (start) {
if (!(thisToken.equalsIgnoreCase(GETQSTART) || thisToken.equalsIgnoreCase(USINGQSTART)))
throw new InvalidQueryException(
"Invalid Query Recieved, should begin with either 'using' or 'get': " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(GETQSTART)) {
start = false;
getClause = true;
} else {
start = false;
usingClause = true;
}
} else if (usingClause) {
if (domainSpecificHandlerAvailable(thisToken))
throw new InvalidQueryException(
"Invalid Query Recieved, domain specific clause before " + GETQSTART + " clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QWHERE))
throw new InvalidQueryException(
"Invalid Query Recieved, where clause before " + GETQSTART + " clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QLIMIT))
throw new InvalidQueryException(
"Invalid Query Recieved, limit clause before " + GETQSTART + " clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QSORT))
throw new InvalidQueryException(
"Invalid Query Recieved, sortBy clause before " + GETQSTART + " clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(GETQSTART)) {
usingClause = false;
getClause = true;
} else {
if (thisDatasetConfig != null)
throw new InvalidQueryException(
"Invalid Query Recieved, DatasetConfig already set, attempted to set again: " + newquery + "\n");
else {
DatasetRequest dsrq = new DatasetRequest(thisToken, this);
try {
if (envMart == null || !(envMart.getName().equals(dsrq.mart)))
query.setDataSource(adaptorManager.getAdaptorByName(dsrq.mart).getDataSource());
thisDatasetConfig =
adaptorManager.getAdaptorByName(dsrq.mart).getDatasetConfigByDatasetInternalName(dsrq.dataset, dsrq.datasetconfig);
query.setDataset(thisDatasetConfig.getDataset());
} catch (ConfigurationException e1) {
throw new InvalidQueryException("Could not set parse using request " + thisToken + "\n");
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("setting local dataset to " + thisDatasetConfig.getDataset() + "\n");
if (query.getDataSource() != null)
logger.fine("setting Mart to " + query.getDataSource().getName() + "\n");
}
}
}
} else if (getClause) {
// set dataset and update query with starbases, or throw an exception if dataset not set
if (thisDatasetConfig == null) {
if (envDataset == null) {
throw new InvalidQueryException(
"Invalid Query Recieved, did not set DatasetConfig: "
+ newquery
+ "\nEither set environmental DatasetConfig with 'set' or 'use', or use a 'using' clause in your MQL\n");
} else
thisDatasetConfig = envDataset;
}
query.setDataset(thisDatasetConfig.getDataset());
query.setMainTables(thisDatasetConfig.getStarBases());
query.setPrimaryKeys(thisDatasetConfig.getPrimaryKeys());
//favor using DataSource over envMart
if (query.getDataSource() == null) {
if (envMart != null)
query.setDataSource(envMart);
else
throw new InvalidQueryException(
"Invalid Query Recieved, could not get a Mart from a 'using' clause, or the environment: "
+ newquery
+ "\n");
}
if (thisToken.equalsIgnoreCase(GETQSTART) || thisToken.equalsIgnoreCase(USINGQSTART))
throw new InvalidQueryException(
"Invalid Query Recieved, "
+ GETQSTART
+ " clause in the middle of a "
+ GETQSTART
+ " clause: "
+ newquery
+ "\n");
else if (thisToken.equalsIgnoreCase(QLIMIT)) {
if (!validQuery)
throw new InvalidQueryException("Recieved invalid Query " + newquery + "\ncheck for a dangling comma\n");
validQuery = false;
getClause = false;
limitClause = true;
} else if (thisToken.equalsIgnoreCase(QSORT)) {
if (!validQuery)
throw new InvalidQueryException("Recieved invalid Query " + newquery + "\ncheck for a dangling comma\n");
if (!advancedFeaturesOn)
throw new InvalidQueryException("sortBy request not allowed unless advancedFeatures set\n");
validQuery = false;
getClause = false;
sortClause = true;
} else if (domainSpecificHandlerAvailable(thisToken)) {
validQuery = false;
domainSpecificKeyword = thisToken;
getClause = false;
domainSpecificClause = true;
} else if (thisToken.equalsIgnoreCase(QWHERE)) {
if (logger.isLoggable(Level.FINE))
logger.fine("Recieved where clause after attributes, query is valid: " + validQuery + " \n");
if (!validQuery)
throw new InvalidQueryException("Recieved invalid Query " + newquery + "\ncheck for a dangling comma\n");
validQuery = false;
getClause = false;
whereClause = true;
whereFilterName = true;
} else {
if (thisToken.endsWith(",")) {
if (logger.isLoggable(Level.FINE))
logger.fine(thisToken + " Comma, setting validQuery to false\n");
thisToken = thisToken.substring(0, thisToken.length() - 1);
validQuery = false;
} else {
if (logger.isLoggable(Level.FINE))
logger.fine(thisToken + " Not comma, setting validQuery to true\n");
validQuery = true;
}
StringTokenizer attToks = new StringTokenizer(thisToken, ",");
while (attToks.hasMoreTokens())
query = addAttribute(query, thisDatasetConfig, attToks.nextToken().trim());
}
} else if (domainSpecificClause) {
if (thisToken.equalsIgnoreCase(GETQSTART) || thisToken.equalsIgnoreCase(USINGQSTART))
throw new InvalidQueryException(
"Invalid Query Recieved, " + GETQSTART + " clause in the middle of a sequence clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QLIMIT)) {
if (!validQuery)
throw new InvalidQueryException(
"Recieved invalid Query " + newquery + "\ncheck for an incomplete Domain Specific Request\n");
validQuery = false;
domainSpecificClause = false;
limitClause = true;
} else if (thisToken.equalsIgnoreCase(QSORT)) {
if (!validQuery)
throw new InvalidQueryException("Recieved invalid Query " + newquery + "\ncheck for a dangling comma\n");
if (!advancedFeaturesOn)
throw new InvalidQueryException("sortBy request not allowed unless advancedFeatures set\n");
validQuery = false;
getClause = false;
sortClause = true;
} else if (thisToken.equalsIgnoreCase(QWHERE)) {
if (!validQuery)
throw new InvalidQueryException(
"Recieved invalid Query " + newquery + "\ncheck for an incomplete Domain Specific Request\n");
validQuery = false;
domainSpecificClause = false;
whereClause = true;
whereFilterName = true;
} else {
query = modifyQueryForDomainSpecificKeyword(domainSpecificKeyword, query, thisDatasetConfig, thisToken);
validQuery = true;
}
} else if (whereClause) {
if (thisToken.equalsIgnoreCase(QLIMIT)) {
if (!validQuery)
throw new InvalidQueryException(
"Recieved invalid Query "
+ newquery
+ "\ncheck for a dangling filter delimiter "
+ FILTERDELIMITER
+ "\n");
validQuery = false;
whereClause = false;
limitClause = true;
} else if (thisToken.equalsIgnoreCase(QSORT)) {
if (!validQuery)
throw new InvalidQueryException(
"Recieved invalid Query "
+ newquery
+ "\ncheck for a dangling filter delimiter "
+ FILTERDELIMITER
+ "\n");
if (!advancedFeaturesOn)
throw new InvalidQueryException("sortBy request not allowed unless advancedFeatures set\n");
validQuery = false;
whereClause = false;
sortClause = true;
} else if (thisToken.equalsIgnoreCase(GETQSTART) || thisToken.equalsIgnoreCase(USINGQSTART))
throw new InvalidQueryException(
"Invalid Query Recieved, " + GETQSTART + " clause after where clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QWHERE))
throw new InvalidQueryException(
"Invalid Query Recieved, where clause after where clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(FILTERDELIMITER) && !inQuotedValue) {
whereFilterCond = false;
whereFilterVal = false;
whereFilterName = true;
validQuery = false;
} else if (whereFilterName) {
if (thisToken.matches("[^>=<]+([>=<]+)[^>=<]*")) {
Pattern pat = Pattern.compile("[^>=<]+([>=<]+)[^>=<]*");
//one or more non qualifier characters followed immediately by a qualifier, followed by zero or more non qualifier characters
Matcher m = pat.matcher(thisToken);
m.find(); // know its there, just have to find it
filterCondition = m.group(1);
whereFilterCond = false;
StringTokenizer filtToks = new StringTokenizer(thisToken, filterCondition);
if (filtToks.countTokens() == 2) {
filterName = filtToks.nextToken();
whereFilterName = false;
String valTok = filtToks.nextToken();
whereFilterVal = true;
if (valTok.startsWith(QUOTE) || valTok.startsWith(QUOTEESCSTART)) {
if (valTok.startsWith(QUOTEESCSTART))
valTok = valTok.substring(2);
else
valTok = valTok.substring(1);
inQuotedValue = true;
if (valTok.endsWith(QUOTE) || valTok.endsWith(QUOTEESCEND)) {
valTok = valTok.substring(0, valTok.length() - 1);
query = addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, valTok);
validQuery = true;
inQuotedValue = false;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterVal = false;
} else
filterValue.append(valTok);
} else {
query =
addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, valTok);
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterVal = false;
}
} else {
filterName = filtToks.nextToken();
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = true;
}
} else {
filterName = thisToken;
whereFilterName = false;
whereFilterCond = true;
whereFilterVal = false;
}
} else if (whereFilterCond) {
if (BOOLEANQUALIFIERS.contains(thisToken)) {
query = addBooleanFilter(query, thisDatasetConfig, filterName, thisToken);
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
} else if (thisToken.matches("([>=<]+)([^>=<]+)")) {
Pattern p = Pattern.compile("([>=<]+)([^>=<]+)");
Matcher m = p.matcher(thisToken);
m.find();
filterCondition = m.group(1);
whereFilterCond = false;
if (!ALLQUALIFIERS.contains(filterCondition))
throw new InvalidQueryException(
"Recieved invalid FilterCondition " + filterCondition + " in " + newquery + "\n");
String valTok = m.group(2);
whereFilterVal = true;
if (valTok.startsWith(QUOTE) || valTok.startsWith(QUOTEESCSTART)) {
if (valTok.startsWith(QUOTEESCSTART))
valTok = valTok.substring(2);
else
valTok = valTok.substring(1);
inQuotedValue = true;
whereFilterName = false;
whereFilterCond = false;
if (valTok.endsWith(QUOTE) || valTok.endsWith(QUOTEESCEND)) {
valTok = valTok.substring(0, valTok.length() - 1);
query = addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, valTok);
validQuery = true;
inQuotedValue = false;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterVal = false;
} else
filterValue.append(valTok);
} else {
query = addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, valTok);
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
}
} else {
filterCondition = thisToken;
if (!ALLQUALIFIERS.contains(filterCondition))
throw new InvalidQueryException(
"Recieved invalid FilterCondition " + filterCondition + " in " + newquery + "\n");
whereFilterCond = false;
whereFilterVal = true;
}
} else if (whereFilterVal) {
if (thisToken.startsWith(QUOTE) || thisToken.startsWith(QUOTEESCSTART)) {
String tok = null;
if (thisToken.startsWith(QUOTEESCSTART))
tok = thisToken.substring(2);
else
tok = thisToken.substring(1);
inQuotedValue = true;
whereFilterName = false;
whereFilterCond = false;
if (thisToken.endsWith(QUOTE) || thisToken.endsWith(QUOTEESCEND)) {
tok = tok.substring(0, tok.length() - 1);
query = addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, tok);
validQuery = true;
inQuotedValue = false;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterVal = false;
} else
filterValue.append(tok);
} else if (inQuotedValue) {
if (thisToken.endsWith(QUOTE) || thisToken.endsWith(QUOTEESCEND)) {
filterValue.append(" ").append(thisToken.substring(0, thisToken.length() - 1));
query = addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, filterValue.toString());
validQuery = true;
inQuotedValue = false;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
} else
filterValue.append(" ").append(thisToken);
} else if (thisToken.equals(LSTART)) {
inList = true;
} else if (thisToken.startsWith(LSTART)) {
inList = true;
String tmp = thisToken.substring(1);
if (tmp.indexOf(LEND) > 0) {
inList = false;
tmp = tmp.substring(0, tmp.indexOf(LEND));
StringTokenizer toks = new StringTokenizer(tmp, ",");
while (toks.hasMoreTokens())
listFilterValues.add(toks.nextToken().trim());
query = addListFilter(query, thisDatasetConfig, filterName, listFilterValues);
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
listFilterValues = new ArrayList();
} else {
if (tmp.indexOf(",") >= 0) {
StringTokenizer toks = new StringTokenizer(tmp, ",");
while (toks.hasMoreTokens())
listFilterValues.add(toks.nextToken().trim());
} else {
listFilterValues.add(tmp);
}
}
} else if (inList) {
if (thisToken.indexOf(LEND) >= 0) {
inList = false;
String tmp = thisToken.substring(0, thisToken.indexOf(LEND));
if (tmp.length() > 0) {
StringTokenizer toks = new StringTokenizer(tmp, ",");
while (toks.hasMoreTokens())
listFilterValues.add(toks.nextToken().trim());
}
query = addListFilter(query, thisDatasetConfig, filterName, listFilterValues);
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
listFilterValues = new ArrayList();
} else {
StringTokenizer toks = new StringTokenizer(thisToken, ",");
while (toks.hasMoreTokens())
listFilterValues.add(toks.nextToken().trim());
}
} else if (thisToken.indexOf(LSTART) > 0) {
if (thisToken.endsWith(LEND)) {
// storedCommand with bindValues, no whitespaces
query = addListFilter(query, thisDatasetConfig, filterName, thisToken);
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
inBind = false;
storedCommand = new StringBuffer();
} else {
//append to storedCommand
inBind = true;
if (storedCommand.length() > 0)
storedCommand.append(" ");
storedCommand.append(thisToken);
}
} else if (inBind) {
if (thisToken.endsWith(LEND)) {
// add storedCommand
query =
addListFilter(
query,
thisDatasetConfig,
filterName,
storedCommand.append(" ").append(thisToken).toString());
validQuery = true;
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
inBind = false;
storedCommand = new StringBuffer();
} else {
//append to storedCommand
if (storedCommand.length() > 0)
storedCommand.append(" ");
storedCommand.append(thisToken);
}
} else {
if (filterCondition.equalsIgnoreCase("in")) {
if (thisToken.indexOf(":") >= 0) {
//url
try {
query = addListFilter(query, thisDatasetConfig, filterName, new URL(thisToken));
validQuery = true;
} catch (Exception e) {
throw new InvalidQueryException(
"Error adding url filter " + filterName + " " + thisToken + " " + e.getMessage(),
e);
}
} else if (storedCommands.containsKey(thisToken)) {
//storedCommand without bindvalues
query = addListFilter(query, thisDatasetConfig, filterName, thisToken);
validQuery = true;
} else {
//file
query = addListFilter(query, thisDatasetConfig, filterName, new File(thisToken));
validQuery = true;
}
} else {
query = addBasicFilter(query, thisDatasetConfig, filterName, filterCondition, thisToken);
validQuery = true;
}
filterValue = new StringBuffer();
filterName = null;
filterCondition = null;
whereFilterName = false;
whereFilterCond = false;
whereFilterVal = false;
}
} else
throw new InvalidQueryException(
"Invalid Query Recieved, invalid filter statement in where clause: " + newquery + "\n");
} else if (sortClause) {
if (thisToken.equalsIgnoreCase(GETQSTART) || thisToken.equalsIgnoreCase(USINGQSTART))
throw new InvalidQueryException(
"Invalid Query Recieved, " + GETQSTART + " clause in limit clause: " + newquery + "\n");
else if (domainSpecificHandlerAvailable(thisToken))
throw new InvalidQueryException(
"Invalid Query Recieved, domain specific clause in limit clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QWHERE))
throw new InvalidQueryException("Invalid Query Recieved, where clause in limit clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QLIMIT)) {
if (!validQuery)
throw new InvalidQueryException(
"Recieved invalid Query " + newquery + "\ncheck for an incomplete Domain Specific Request\n");
validQuery = false;
sortClause = false;
limitClause = true;
} else {
if (thisToken.endsWith(",")) {
if (logger.isLoggable(Level.FINE))
logger.fine(thisToken + " Comma, setting validQuery to false\n");
thisToken = thisToken.substring(0, thisToken.length() - 1);
validQuery = false;
} else {
if (logger.isLoggable(Level.FINE))
logger.fine(thisToken + " Not comma, setting validQuery to true\n");
validQuery = true;
}
StringTokenizer attToks = new StringTokenizer(thisToken, ",");
while (attToks.hasMoreTokens())
query = addSortAttribute(query, thisDatasetConfig, attToks.nextToken().trim());
validQuery = true;
}
} else if (limitClause) {
if (thisToken.equalsIgnoreCase(GETQSTART) || thisToken.equalsIgnoreCase(USINGQSTART))
throw new InvalidQueryException(
"Invalid Query Recieved, " + GETQSTART + " clause in limit clause: " + newquery + "\n");
else if (domainSpecificHandlerAvailable(thisToken))
throw new InvalidQueryException(
"Invalid Query Recieved, domain specific clause in limit clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QWHERE))
throw new InvalidQueryException("Invalid Query Recieved, where clause in limit clause: " + newquery + "\n");
else if (thisToken.equalsIgnoreCase(QSORT))
throw new InvalidQueryException("Invalid Query Recieved, sortBy clause in limit clause: " + newquery + "\n");
else {
if (query.getLimit() > 0)
throw new InvalidQueryException("Invalid Query Recieved, attempt to set limit twice: " + newquery + "\n");
else {
query.setLimit(Integer.parseInt(thisToken));
validQuery = true;
}
}
}
// else not needed, as these are the only states present
}
if (!validQuery)
throw new InvalidQueryException(
"Recieved invalid query "
+ newquery
+ "\ncheck for dangling commas between attributes, an incomplete domain specific request, a dangling filter delimeter "
+ FILTERDELIMITER
+ " between filter requests,\nor an incomplete limit request\n");
if (query.getAttributes().length == 0 && query.getSequenceDescription() == null)
throw new InvalidQueryException(
"Invalid Query Recieved, no attributes or sequence description found " + newquery + "\n");
//GenericHandler requires the query to have a DatasetConfig
query.setDatasetConfig(thisDatasetConfig);
return query;
} catch (NumberFormatException e) {
throw new InvalidQueryException("Recieved NumberFormatException parsing MQL " + e.getMessage(), e);
} catch (InvalidQueryException e) {
throw e;
}
}
private Query modifyQueryForDomainSpecificKeyword(
String domainSpecificKeyword,
Query query,
DatasetConfig dset,
String thisToken)
throws InvalidQueryException {
// can either add keywords here, or replace it with a Plugin Module
return addSequenceDescription(query, dset, thisToken);
}
private boolean domainSpecificHandlerAvailable(String keyword) throws InvalidQueryException {
//modify this to add other domainSpecific keywords, or just replace it with a module
if (keyword.equalsIgnoreCase(QSEQUENCE))
return true;
return false;
}
private boolean domainSpecificSubQueryAllowed(String keyword) {
// modify this to add other domainSpecific keywords, or just replace it with a module
return !QSEQUENCE.equalsIgnoreCase(keyword);
}
private synchronized Filter getIDFilterForSubQuery(
String fieldName,
String tableConstraint,
String key,
String storedCommandName)
throws InvalidQueryException {
String bindValues = null;
if (storedCommandName.indexOf(LSTART) > 0) {
bindValues = storedCommandName.substring(storedCommandName.indexOf(LSTART) + 1, storedCommandName.indexOf(LEND));
storedCommandName = storedCommandName.substring(0, storedCommandName.indexOf(LSTART));
}
if (!storedCommands.containsKey(storedCommandName))
throw new InvalidQueryException(storedCommandName + " is not available as a stored MQL Command\n");
String nestedQuery = storedCommands.getProperty(storedCommandName);
if ((bindValues != null) && (bindValues.length() > 0)) {
List bindVariables = new ArrayList();
StringTokenizer vtokens = new StringTokenizer(bindValues, ",");
while (vtokens.hasMoreTokens())
bindVariables.add(vtokens.nextToken().trim());
Pattern bindp = Pattern.compile("\\?");
Matcher bindm = bindp.matcher(nestedQuery);
StringBuffer qbuf = new StringBuffer();
int bindIter = 0;
while (bindm.find()) {
bindm.appendReplacement(qbuf, (String) bindVariables.get(bindIter));
bindIter++;
}
bindm.appendTail(qbuf);
nestedQuery = qbuf.toString();
}
//validate, then call parseQuery on the subcommand
String[] tokens = nestedQuery.split("\\s");
for (int i = 1, n = tokens.length; i < n; i++) {
String tok = tokens[i];
if (domainSpecificHandlerAvailable(tok) && !domainSpecificSubQueryAllowed(tok))
throw new InvalidQueryException(
"Invalid Nested Query Recieved: domain specific statement " + tok + " is not allowed " + nestedQuery + "\n");
//else not needed
}
nestedLevel++;
if (nestedLevel > MAXNESTING) {
nestedLevel--;
throw new InvalidQueryException("Only " + MAXNESTING + " levels of nested Query are allowed\n");
}
Query subQuery = null;
try {
subQuery = MQLtoQuery(nestedQuery);
} catch (Exception e) {
nestedLevel--;
throw new InvalidQueryException("Could not parse Nested Query : " + e.getMessage(), e);
}
subQuery.setQueryName(storedCommandName);
Filter f = null;
//if (handler != null)
// f = new IDListFilter(fieldName, tableConstraint, key, subQuery, handler);
//else
f = new IDListFilter(fieldName, tableConstraint, key, subQuery);
nestedLevel--;
return f;
}
private Query addSequenceDescription(Query inquery, DatasetConfig dset, String seqrequest)
throws InvalidQueryException {
currentApage = dset.getAttributePageByInternalName("sequences");
for (int i = 0, n = atts.size(); i < n; i++) {
String element = (String) atts.get(i);
if (!currentApage.containsAttributeDescription(element))
throw new InvalidQueryException(
"Cannot request attribute " + element + " together with sequences in the same query.\n");
}
Query newQuery = new Query(inquery);
String seqDescription = null;
int left = 0;
int right = 0;
StringTokenizer tokens = new StringTokenizer(seqrequest, SEQDELIMITER, true);
int n = tokens.countTokens();
switch (n) {
case 5 :
// left+type+right
left = Integer.parseInt(tokens.nextToken());
tokens.nextToken(); // skip plus
seqDescription = tokens.nextToken();
tokens.nextToken();
right = Integer.parseInt(tokens.nextToken());
break;
case 3 :
// left+type || type+right
String tmpl = tokens.nextToken();
tokens.nextToken();
String tmpr = tokens.nextToken();
if (dset.containsAttributeDescription(tmpl)) {
seqDescription = tmpl;
right = Integer.parseInt(tmpr);
} else if (dset.containsAttributeDescription(tmpr)) {
left = Integer.parseInt(tmpl);
seqDescription = tmpr;
} else {
throw new InvalidQueryException("Invalid sequence request recieved: " + seqrequest + "\n");
}
break;
case 1 :
// type
seqDescription = seqrequest;
break;
}
AttributeDescription attrDesc = currentApage.getAttributeDescriptionByInternalName(seqDescription);
String seqDs = attrDesc.getPointerDataset();
if (seqDs==null || "".equals(seqDs)) seqDs = dset.getDataset();
newQuery.setSequenceDescription(new SequenceDescription(dset.getDataset(), seqDs, seqDescription, adaptorManager, left, right));
return newQuery;
}
private Query addAttribute(Query inquery, DatasetConfig dset, String attname) throws InvalidQueryException {
checkAttributeValidity(dset, attname);
Query newQuery = new Query(inquery);
AttributeDescription attdesc = (AttributeDescription) dset.getAttributeDescriptionByInternalName(attname);
Attribute attr = null;
if (attdesc.getPointerDataset()!=null && !"".equals(attdesc.getPointerDataset())) {
//placeholder sequence attribute
attr = new FieldAttribute(attdesc.getInternalName(), attdesc.getTableConstraint(), attdesc.getKey());
}
else
attr = new FieldAttribute(attdesc.getField(), attdesc.getTableConstraint(), attdesc.getKey());
newQuery.addAttribute(attr);
return newQuery;
}
private Query addSortAttribute(Query inquery, DatasetConfig dset, String attname) throws InvalidQueryException {
checkAttributeValidity(dset, attname);
Query newQuery = new Query(inquery);
AttributeDescription attdesc = (AttributeDescription) dset.getAttributeDescriptionByInternalName(attname);
Attribute attr = null;
attr = new FieldAttribute(attdesc.getField(), attdesc.getTableConstraint(), attdesc.getKey());
newQuery.addSortByAttribute(attr);
return newQuery;
}
private void checkAttributeValidity(DatasetConfig dset, String attname) throws InvalidQueryException {
if (!dset.containsAttributeDescription(attname))
throw new InvalidQueryException(
"Attribute " + attname + " is not found in this mart for dataset " + dset.getInternalName() + "\n");
//check page
if (currentApage == null) {
currentApage = dset.getPageForAttribute(attname);
} else {
if (!currentApage.containsAttributeDescription(attname)) {
currentApage = dset.getPageForAttribute(attname);
for (int i = 0, n = atts.size(); i < n; i++) {
String element = (String) atts.get(i);
if (!currentApage.containsAttributeDescription(element))
throw new InvalidQueryException(
"Cannot request attributes "
+ attname
+ " and "
+ element
+ " together in the same query. Use 'describe dataset "
+ dset.getInternalName()
+ "' for a list of attributes that can be selected together\n");
}
}
}
//check maxSelect
AttributeCollection collection = currentApage.getCollectionForAttributeDescription(attname);
String colname = collection.getInternalName();
int maxSelect = collection.getMaxSelect();
if (maxSelect > 0) {
if (maxSelects.containsKey(colname)) {
int oldMax = ((Integer) maxSelects.get(colname)).intValue();
oldMax++;
if (oldMax > maxSelect)
throw new InvalidQueryException(
"You cannot select more than " + maxSelect + " attributes from AttributeCollection " + colname + "\n");
maxSelects.put(colname, new Integer(oldMax));
} else
maxSelects.put(colname, new Integer(1));
}
atts.add(attname);
}
private Query addBooleanFilter(Query inquery, DatasetConfig dset, String filterName, String filterCondition)
throws InvalidQueryException {
checkFilterValidity(dset, filterName);
FilterDescription fdesc = dset.getFilterDescriptionByInternalName(filterName);
String thisType = fdesc.getType(filterName);
if (!thisType.startsWith("boolean"))
throw new InvalidQueryException(
filterName + " is not a boolean filter, cannot process with " + filterCondition + "\n");
if (!BOOLEANQUALIFIERS.contains(filterCondition))
throw new InvalidQueryException(filterCondition + " is not valid for a boolean filter\n");
String thisCondition = null;
if (thisType.equals("boolean_num"))
thisCondition = BOOLEAN_NUMCONDITIONS[BOOLEANQUALIFIERS.indexOf(filterCondition)];
else
thisCondition = BOOLEAN_CONDITIONS[BOOLEANQUALIFIERS.indexOf(filterCondition)];
//String handler = fdesc.getHandler(filterName);
Query newQuery = new Query(inquery);
newQuery.addFilter(
new BooleanFilter(
fdesc.getField(filterName),
fdesc.getTableConstraint(filterName),
fdesc.getKey(filterName),
thisCondition
//handler)
));
return newQuery;
}
private Query addBasicFilter(
Query inquery,
DatasetConfig dset,
String filterName,
String filterCondition,
String filterValue)
throws InvalidQueryException {
checkFilterValidity(dset, filterName);
FilterDescription fdesc = dset.getFilterDescriptionByInternalName(filterName);
Query newQuery = new Query(inquery);
//if (fdesc.getHandler(filterName) != null) {
// newQuery.addFilter(
// new BasicFilter(
// fdesc.getField(filterName),
// fdesc.getTableConstraint(filterName),
// fdesc.getKey(filterName),
// filterCondition,
// filterValue,
// fdesc.getHandler(filterName)));
//} else {
newQuery.addFilter(
new BasicFilter(
fdesc.getField(filterName),
fdesc.getTableConstraint(filterName),
fdesc.getKey(filterName),
filterCondition,
filterValue));
//}
return newQuery;
}
private Query addListFilter(Query inquery, DatasetConfig dset, String filterName, List filterValues)
throws InvalidQueryException {
checkFilterValidity(dset, filterName);
FilterDescription fdesc = dset.getFilterDescriptionByInternalName(filterName);
Query newQuery = new Query(inquery);
//if (fdesc.getHandler(filterName) != null)
// newQuery.addFilter(
// new IDListFilter(
// fdesc.getField(filterName),
// fdesc.getTableConstraint(filterName),
// fdesc.getKey(filterName),
// (String[]) filterValues.toArray(new String[filterValues.size()]),
// fdesc.getHandler(filterName)));
//else
newQuery.addFilter(
new IDListFilter(
fdesc.getField(filterName),
fdesc.getTableConstraint(filterName),
fdesc.getKey(filterName),
(String[]) filterValues.toArray(new String[filterValues.size()])));
return newQuery;
}
private Query addListFilter(Query inquery, DatasetConfig dset, String filterName, File fileloc)
throws InvalidQueryException {
checkFilterValidity(dset, filterName);
FilterDescription fdesc = dset.getFilterDescriptionByInternalName(filterName);
Query newQuery = new Query(inquery);
//if (fdesc.getHandler(filterName) != null)
// newQuery.addFilter(
// new IDListFilter(
// fdesc.getField(filterName),
// fdesc.getTableConstraint(filterName),
// fdesc.getKey(filterName),
// fileloc,
// fdesc.getHandler(filterName)));
//else
newQuery.addFilter(
new IDListFilter(
fdesc.getField(filterName),
fdesc.getTableConstraint(filterName),
fdesc.getKey(filterName),
fileloc));
return newQuery;
}
private Query addListFilter(Query inquery, DatasetConfig dset, String filterName, URL urlLoc)
throws InvalidQueryException {
checkFilterValidity(dset, filterName);
FilterDescription fdesc = dset.getFilterDescriptionByInternalName(filterName);
Query newQuery = new Query(inquery);
//if (fdesc.getHandler(filterName) != null)
// newQuery.addFilter(
// new IDListFilter(
// fdesc.getField(filterName),
// fdesc.getTableConstraint(filterName),
// fdesc.getKey(filterName),
// urlLoc,
// fdesc.getHandler(filterName)));
//else
newQuery.addFilter(
new IDListFilter(
fdesc.getField(filterName),
fdesc.getTableConstraint(filterName),
fdesc.getKey(filterName),
urlLoc));
return newQuery;
}
private Query addListFilter(Query inquery, DatasetConfig dset, String filterName, String storedQueryName)
throws InvalidQueryException {
checkFilterValidity(dset, filterName);
FilterDescription fdesc = dset.getFilterDescriptionByInternalName(filterName);
Query newQuery = new Query(inquery);
// subquery will overwrite page states, need to return them to original after it is parsed
AttributePage bakApage = currentApage;
FilterPage bakFpage = currentFpage;
currentApage = null;
currentFpage = null;
newQuery.addFilter(
getIDFilterForSubQuery(
fdesc.getField(filterName),
fdesc.getTableConstraint(filterName),
fdesc.getKey(filterName),
//fdesc.getHandler(filterName),
storedQueryName));
currentApage = bakApage;
currentFpage = bakFpage;
return newQuery;
}
private void checkFilterValidity(DatasetConfig dset, String filterName) throws InvalidQueryException {
if (!dset.containsFilterDescription(filterName))
throw new InvalidQueryException(
"Filter " + filterName + " not supported by mart dataset " + dset.getInternalName() + "\n");
if (currentFpage == null)
currentFpage = dset.getPageForFilter(filterName);
else {
if (!currentFpage.containsFilterDescription(filterName)) {
currentFpage = dset.getPageForFilter(filterName);
for (int i = 0, n = filtNames.size(); i < n; i++) {
String element = (String) filtNames.get(i);
if (!currentFpage.containsFilterDescription(element))
throw new InvalidQueryException(
"Cannot use filters "
+ filterName
+ " and "
+ element
+ " together in the same query. Use 'describe dataset "
+ dset.getInternalName()
+ "' to get a list of filters that can be used in the same query.\n");
}
}
}
filtNames.add(filterName);
}
public void setAdvancedFeatures(boolean toggle) {
advancedFeaturesOn = toggle;
}
/**
* Takes a canonicalized martName, and replaces each
* underscore with a single whitespace.
*
* @param String Canonicalized Mart Name
* @return String deCanonicalized Mart Name
*/
public String deCanonicalizeMartName(String cMartName) {
//return cMartName.replaceAll("\\_", " ");
return cMartName;
}
/**
* Takes an uncanonicalized martName, and replaces each
* individual whitespace with a single underscore.
*
* @param String uncanonicalized Mart Name
* @return String canonicalized Mart Name
*/
public String canonicalizeMartName(String cMartName) {
return cMartName.replaceAll(" ", "_");
}
private boolean advancedFeaturesOn = false;
private int maxcharcount = 0;
private String MQLError = null;
//these allow the MartShellLib to act as controller to the MartShell and MartCompleter
protected RegistryDSConfigAdaptor adaptorManager = null;
//dont ignore cache, dont validate, and dont include hidden Members (these are for MartEditor only)
protected DatasetConfig envDataset = null;
protected DetailedDataSource envMart = null;
protected DatasetConfig localDataset = null;
protected boolean usingLocalDataset = false;
private AttributePage currentApage = null;
// keeps track of the AttributePage
private FilterPage currentFpage = null; // keeps track of the FilterPage
// will hold max-select keyed by collection.internalName
private Hashtable maxSelects = new Hashtable();
// will hold all previously selected UIAttributeDescriptions, for page constraint validation during addAttribute
private List atts = new ArrayList();
private List filtNames = new ArrayList();
// query instructions
public static final String GETQSTART = "get";
public static final String USINGQSTART = "using";
public static final String QSEQUENCE = "sequence";
public static final String QWHERE = "where";
public static final String QLIMIT = "limit";
public static final String QSORT = "sortBy";
public static final char LISTSTARTCHR = '(';
private final String LSTART = String.valueOf(LISTSTARTCHR);
private final String QUOTE = "'";
private final String QUOTEESCSTART = "q(";
private final String QUOTEESCEND = ")";
public static final char LISTENDCHR = ')';
private final String LEND = String.valueOf(LISTENDCHR);
private final String SEQDELIMITER = "+";
public static final String FILTERDELIMITER = "and";
private final String DEFAULTURLADAPTORNAME = "userfiles";
public static final String DEFAULTDATASETCONFIGNAME = "default";
private final String LISTALLREQ = "all";
private final String MARTREQ = "Mart";
protected final List availableCommands =
Collections.unmodifiableList(Arrays.asList(new String[] { USINGQSTART, GETQSTART }));
// variables for subquery
private int nestedLevel = 0;
private final int MAXNESTING = 1;
// change this to allow deeper nesting of queries inside queries
//Pattern for stored Command
public static final Pattern STOREPAT = Pattern.compile("(.*)\\s+(a|A)(s|S)\\s+(\\w+)$", Pattern.DOTALL);
public static List ALLQUALIFIERS =
Arrays.asList(new String[] { "=", "!=", "<", ">", "<=", ">=", "only", "excluded", "in", "like" });
//TODO: need to make MartShell get a union set of all possible qualifiers from DatasetConfig
public static List BOOLEANQUALIFIERS = Arrays.asList(new String[] { "only", "excluded" });
public final String[] BOOLEAN_NUMCONDITIONS = { BooleanFilter.isNotNULL_NUM, BooleanFilter.isNULL_NUM };
public final String[] BOOLEAN_CONDITIONS = { BooleanFilter.isNotNULL, BooleanFilter.isNULL };
private Logger logger = Logger.getLogger(MartShellLib.class.getName());
private Properties storedCommands = new Properties();
}