package com.workingdogs.village;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* The Schema object represents the <a href="Column.html">Columns</a> in a database table. It contains a collection of <a
* href="Column.html">Column</a> objects.
*
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
* @author John D. McNally
* @version $Revision: 568 $
*/
public final class Schema
{
/** TODO: DOCUMENT ME! */
private String tableName;
/** TODO: DOCUMENT ME! */
private String columnsAttribute;
/** TODO: DOCUMENT ME! */
private int numberOfColumns;
/** TODO: DOCUMENT ME! */
private Column [] columns;
/** TODO: DOCUMENT ME! */
private static Hashtable schemaCache = new Hashtable();
/**
* This attribute is used to complement columns in the event that this schema represents more than one table. Its keys are
* String contains table names and its elements are Hashtables containing columns.
*/
private Hashtable tableHash = null;
/** TODO: DOCUMENT ME! */
private boolean singleTable = true;
/**
* A blank Schema object
*/
public Schema()
{
this.tableName = "";
this.columnsAttribute = null;
this.numberOfColumns = 0;
}
/**
* Creates a Schema with all columns
*
* @param conn
* @param tableName
*
* @return an instance of myself
*
* @exception SQLException
* @exception DataSetException
*/
public Schema schema(Connection conn, String tableName)
throws SQLException, DataSetException
{
return schema(conn, tableName, "*");
}
/**
* Creates a Schema with the named columns in the columnsAttribute
*
* @param conn
* @param tableName
* @param columnsAttribute
*
* @return an instance of myself
*
* @exception SQLException
* @exception DataSetException
*/
public synchronized Schema schema(Connection conn, String tableName, String columnsAttribute)
throws SQLException, DataSetException
{
if (columnsAttribute == null)
{
columnsAttribute = "*";
}
Statement stmt = null;
try
{
String keyValue = conn.getMetaData().getURL() + tableName;
Schema tableSchema = (Schema) schemaCache.get(keyValue);
if (tableSchema == null)
{
String sql = "SELECT " + columnsAttribute + " FROM " + tableName + " WHERE 1 = -1";
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
if (rs != null)
{
tableSchema = new Schema();
tableSchema.setTableName(tableName);
tableSchema.setAttributes(columnsAttribute);
tableSchema.populate(rs.getMetaData(), tableName);
schemaCache.put(keyValue, tableSchema);
}
else
{
throw new DataSetException("Couldn't retrieve schema for " + tableName);
}
}
return tableSchema;
}
finally
{
if (stmt != null)
{
stmt.close();
}
}
}
/**
* Appends data to the tableName that this schema was first created with.
*
* <P></p>
*
* @param app String to append to tableName
*
* @see TableDataSet#tableQualifier(java.lang.String)
*/
void appendTableName(String app)
{
this.tableName = this.tableName + " " + app;
}
/**
* List of columns to select from the table
*
* @return the list of columns to select from the table
*/
public String attributes()
{
return this.columnsAttribute;
}
/**
* Returns the requested Column object at index i
*
* @param i
*
* @return the requested column
*
* @exception DataSetException
*/
public Column column(int i)
throws DataSetException
{
if (i == 0)
{
throw new DataSetException("Columns are 1 based");
}
else if (i > numberOfColumns)
{
throw new DataSetException("There are only " + numberOfColumns() + " available!");
}
try
{
return columns[i];
}
catch (Exception e)
{
throw new DataSetException("Column number: " + numberOfColumns() + " does not exist!");
}
}
/**
* Returns the requested Column object by name
*
* @param colName
*
* @return the requested column
*
* @exception DataSetException
*/
public Column column(String colName)
throws DataSetException
{
return column(index(colName));
}
/**
* Returns the requested Column object by name
*
* @param colName
*
* @return the requested column
*
* @exception DataSetException
*/
public Column getColumn(String colName)
throws DataSetException
{
int dot = colName.indexOf('.');
if (dot > 0)
{
String table = colName.substring(0, dot);
String col = colName.substring(dot + 1);
return getColumn(table, col);
}
return column(index(colName));
}
/**
* Returns the requested Column object belonging to the specified table by name
*
* @param tableName
* @param colName
*
* @return the requested column, null if a column by the specified name does not exist.
*
* @exception DataSetException
*/
public Column getColumn(String tableName, String colName)
throws DataSetException
{
return (Column) ((Hashtable) tableHash.get(tableName)).get(colName);
}
/**
* Returns an array of columns
*
* @return an array of columns
*/
Column [] getColumns()
{
return this.columns;
}
/**
* returns the table name that this Schema represents
*
* @return the table name that this Schema represents
*
* @throws DataSetException TODO: DOCUMENT ME!
*/
public String getTableName()
throws DataSetException
{
if (singleTable)
{
return tableName;
}
else
{
throw new DataSetException("This schema represents several tables.");
}
}
/**
* returns all table names that this Schema represents
*
* @return the table names that this Schema represents
*/
public String [] getAllTableNames()
{
Enumeration e = tableHash.keys();
String [] tableNames = new String[tableHash.size()];
for (int i = 0; e.hasMoreElements(); i++)
{
tableNames[i] = (String) e.nextElement();
}
return tableNames;
}
/**
* Gets the index position of a named column. If multiple tables are represented and they have columns with the same name,
* this method returns the first one listed, if the table name is not specified.
*
* @param colName
*
* @return the requested column index integer
*
* @exception DataSetException
*/
public int index(String colName)
throws DataSetException
{
int dot = colName.indexOf('.');
if (dot > 0)
{
String table = colName.substring(0, dot);
String col = colName.substring(dot + 1);
return index(table, col);
}
for (int i = 1; i <= numberOfColumns(); i++)
{
if (columns[i].name().equalsIgnoreCase(colName))
{
return i;
}
}
throw new DataSetException("Column name: " + colName + " does not exist!");
}
/**
* Gets the index position of a named column.
*
* @param tableName
* @param colName
*
* @return the requested column index integer
*
* @exception DataSetException
*/
public int index(String tableName, String colName)
throws DataSetException
{
for (int i = 1; i <= numberOfColumns(); i++)
{
if (columns[i].name().equalsIgnoreCase(colName) && columns[i].getTableName().equalsIgnoreCase(tableName))
{
return i;
}
}
throw new DataSetException("Column name: " + colName + " does not exist!");
}
/**
* Checks to see if this DataSet represents one table in the database.
*
* @return true if only one table is represented, false otherwise.
*/
public boolean isSingleTable()
{
return singleTable;
}
/**
* Gets the number of columns in this Schema
*
* @return integer number of columns
*/
public int numberOfColumns()
{
return this.numberOfColumns;
}
/**
* Internal method which populates this Schema object with Columns.
*
* @param meta The meta data of the ResultSet used to build this Schema.
* @param tableName The name of the table referenced in this schema, or null if unknown or multiple tables are involved.
*
* @exception SQLException
* @exception DataSetException
*/
void populate(ResultSetMetaData meta, String tableName)
throws SQLException, DataSetException
{
this.numberOfColumns = meta.getColumnCount();
columns = new Column[numberOfColumns() + 1];
for (int i = 1; i <= numberOfColumns(); i++)
{
Column col = new Column();
col.populate(meta, i, tableName);
columns[i] = col;
if ((i > 1) && !col.getTableName().equalsIgnoreCase(columns[i - 1].getTableName()))
{
singleTable = false;
}
}
// Avoid creating a Hashtable in the most common case where only one
// table is involved, even though this makes the multiple table case
// more expensive because the table/column info is duplicated.
if (singleTable)
{
// If available, use a the caller supplied table name.
if ((tableName != null) && (tableName.length() > 0))
{
setTableName(tableName);
}
else
{
// Since there's only one table involved, attempt to set the
// table name to that of the first column. Sybase jConnect
// 5.2 and older will fail, in which case we are screwed.
try
{
setTableName(meta.getTableName(1));
}
catch (Exception e)
{
setTableName("");
}
}
}
else
{
tableHash = new Hashtable((int) ((1.25 * numberOfColumns) + 1));
for (int i = 1; i <= numberOfColumns(); i++)
{
if (tableHash.containsKey(columns[i].getTableName()))
{
((Hashtable) tableHash.get(columns[i].getTableName())).put(columns[i].name(), columns[i]);
}
else
{
Hashtable columnHash = new Hashtable((int) ((1.25 * numberOfColumns) + 1));
columnHash.put(columns[i].name(), columns[i]);
tableHash.put(columns[i].getTableName(), columnHash);
}
}
}
}
/**
* Sets the columns to select from the table
*
* @param attributes comma separated list of column names
*/
void setAttributes(String attributes)
{
this.columnsAttribute = attributes;
}
/**
* Sets the table name that this Schema represents
*
* @param tableName
*/
void setTableName(String tableName)
{
this.tableName = tableName;
}
/**
* returns the table name that this Schema represents
*
* @return the table name that this Schema represents
*
* @throws DataSetException TODO: DOCUMENT ME!
*/
public String tableName()
throws DataSetException
{
return getTableName();
}
/**
* This returns a representation of this Schema
*
* @return a string
*/
public String toString()
{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
PrintWriter out = new PrintWriter(bout);
out.print('{');
for (int i = 1; i <= numberOfColumns; i++)
{
out.print('\'');
if (!singleTable)
{
out.print(columns[i].getTableName() + '.');
}
out.print(columns[i].name() + '\'');
if (i < numberOfColumns)
{
out.print(',');
}
}
out.print('}');
out.flush();
return bout.toString();
}
}
|