/*
*
* SchemaCrawler
* http://sourceforge.net/projects/schemacrawler
* Copyright (c) 2000-2009, Sualeh Fatehi.
*
* 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 schemacrawler.schemacrawler;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import schemacrawler.schema.Table;
import schemacrawler.utility.Utility;
/**
* A SQL query. May be parameterized with ant-like variable references.
*
* @author sfatehi
*/
public final class Query
implements Serializable
{
private static final long serialVersionUID = 2820769346069413473L;
/**
* Interpolate substrings into system property values. Substrings of
* the form ${<i>propname</i>} are interpolated into the text of the
* system property whose key matches <i>propname</i>. For example,
* expandProperties("hello.${user.name}.world") is "hello.foo.world"
* when called by a user named "foo". Property substrings can be
* nested. References to nonexistent system properties are
* interpolated to an empty string.
*
* @param template
* Template
* @return Expanded template
*/
private static String expandTemplateFromProperties(final String template)
{
return expandTemplateFromProperties(template, System.getProperties());
}
/**
* Interpolate substrings into property values. Substrings of the form
* ${<i>propname</i>} are interpolated into the text of the system
* property whose key matches <i>propname</i>. For example,
* expandProperties("hello.${user.name}.world") is "hello.foo.world"
* when called by a user named "foo". Property substrings can be
* nested. References to nonexistent system properties are
* interpolated to an empty string.
*
* @param template
* Template
* @param properties
* Properties to substitute in the template
* @return Expanded template
*/
private static String expandTemplateFromProperties(final String template,
final Properties properties)
{
if (template == null)
{
return null;
}
String expandedTemplate = template;
for (int left; (left = expandedTemplate.indexOf("${")) >= 0;)
{
final int inner = expandedTemplate.indexOf("${", left + 2);
final int right = expandedTemplate.indexOf("}", left + 2);
if (inner >= 0 && inner < right)
{
// Evaluate nested property value
expandedTemplate = expandedTemplate.substring(0, inner)
+ expandTemplateFromProperties(expandedTemplate
.substring(inner));
}
else if (right >= 0)
{
// Evaluate this property value
final String propertyKey = expandedTemplate.substring(left + 2, right);
Object propertyValue = properties.get(propertyKey);
if (propertyValue == null)
{
propertyValue = "";
}
expandedTemplate = expandedTemplate.substring(0, left) + propertyValue
+ expandedTemplate.substring(right + 1);
}
else
{
// Unmatched left delimiter - ignore
break;
}
}
return expandedTemplate;
}
/**
* Gets a list of template variables.
*
* @param template
* Template to extract variables from
* @return Set of variables
*/
private static Set<String> extractTemplateVariables(final String template)
{
if (template == null)
{
return new HashSet<String>();
}
String shrunkTemplate = template;
final Set<String> keys = new HashSet<String>();
for (int left; (left = shrunkTemplate.indexOf("${")) >= 0;)
{
final int right = shrunkTemplate.indexOf("}", left + 2);
if (right >= 0)
{
final String propertyKey = shrunkTemplate.substring(left + 2, right);
keys.add(propertyKey);
// Destroy key, so we can find the next one
shrunkTemplate = shrunkTemplate.substring(0, left) + ""
+ shrunkTemplate.substring(right + 1);
}
}
return keys;
}
private final String name;
private final String query;
/**
* Definition of a query, including a name, and parameterized or
* regular SQL.
*
* @param name
* Query name.
* @param query
* Query SQL.
*/
public Query(final String name, final String query)
{
if (Utility.isBlank(name))
{
throw new IllegalArgumentException("No name provided for the query");
}
this.name = name;
if (query == null || query.length() == 0)
{
throw new IllegalArgumentException("No SQL provided for query '" + name
+ "'");
}
this.query = query;
}
/**
* Gets the query name.
*
* @return Query name
*/
public String getName()
{
return name;
}
/**
* Gets the query SQL.
*
* @return Query SQL
*/
public String getQuery()
{
return expandTemplateFromProperties(query);
}
/**
* Gets the query with parameters substituted.
*
* @param table
* Table information
* @return Ready-to-execute quer
*/
public String getQueryForTable(final Table table)
{
final Properties tableProperties = new Properties();
if (table != null)
{
if (table.getSchema() != null)
{
tableProperties.setProperty("catalog", String.valueOf(table.getSchema()
.getParent().getName()));
tableProperties.setProperty("schema", table.getSchema().getName());
}
tableProperties.setProperty("table", table.getFullName());
tableProperties.setProperty("columns", table.getColumnsListAsString());
tableProperties.setProperty("tabletype", table.getType().toString());
}
String sql = query;
sql = expandTemplateFromProperties(sql, tableProperties);
sql = expandTemplateFromProperties(sql);
return sql;
}
/**
* Determines if this query has substitutable parameters, and whether
* it should be run once for each table.
*
* @return If the query is to be run over each table
*/
public boolean isQueryOver()
{
final Set<String> keys = extractTemplateVariables(query);
return keys.contains("table");
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return name + ":" + query;
}
}