/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotoolkit.metadata.sql;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.geotoolkit.resources.Errors;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.internal.sql.StatementEntry;
/**
* The result of a query for metadata attributes. This object {@linkplain PreparedStatement
* prepares a statement} once for ever for a given table. When a particular record in this
* table is fetched, the {@link ResultSet} is automatically constructed. If many attributes
* are fetched consecutively for the same record, then the same {@link ResultSet} is reused.
*
* {@section Synchronization}
* This class is <strong>not</strong> thread-safe. Callers must perform their own synchronization
* in such a way that only one query is executed on the same connection (JDBC connections can not
* be assumed thread-safe).
*
* @author Touraïvane (IRD)
* @author Martin Desruisseaux (IRD)
* @version 3.03
*
* @since 3.03 (derived from 2.1)
* @module
*/
final class MetadataResult extends StatementEntry {
/**
* The implemented interface, used for formatting error messages.
*/
private final Class<?> type;
/**
* The results, or {@code null} if not yet determined.
*/
private ResultSet results;
/**
* The identifier (usually the primary key) for current results.
* If the record to fetch doesn't have the same identifier, then
* the {@link #results} will need to be closed and reconstructed.
*/
private String identifier;
/**
* Constructs a metadata result from the specified connection.
*
* @param type The implemented interface.
* @param statement The prepared statement to be executed.
*/
MetadataResult(final Class<?> type, final PreparedStatement statement) {
super(statement);
this.type = type;
}
/**
* Closes the current {@link ResultSet}. Before doing so, we make an opportunist check for
* duplicated values in the table. If a duplicate is found, a warning is logged. The log
* message pretends to be emitted by the interface constructor, which doesn't exist. But
* this is the closest we can get from a public API. Otherwise the emitter is deep behind
* {@link java.lang.reflect.Proxy}, which generates on-the-fly implementations of the
* interface (so we are not completely wrong neither).
*/
private void closeResultSet() throws SQLException {
final boolean hasNext = results.next();
results.close();
results = null;
if (hasNext) {
final LogRecord record = Errors.getResources(null).getLogRecord(
Level.WARNING, Errors.Keys.DuplicatedValuesForKey_1, identifier);
record.setSourceClassName(type.getCanonicalName());
record.setSourceMethodName("<init>");
Logging.getLogger("org.geotoolkit.sql").log(record);
}
identifier = null;
}
/**
* Returns the result set for the record having the given identifier.
*
* @param identifier The object identifier, usually the primary key value.
* @return The result set.
* @throws SQLException if an SQL operation failed.
*/
private ResultSet getResultSet(final String identifier) throws SQLException {
if (results != null) {
if (this.identifier.equals(identifier)) {
return results;
}
closeResultSet();
}
statement.setString(1, identifier);
results = statement.executeQuery();
if (!results.next()) {
final String table = results.getMetaData().getTableName(1);
results.close();
results = null;
throw new SQLException(Errors.format(Errors.Keys.NoSuchRecord_2, table, identifier));
}
this.identifier = identifier;
return results;
}
/**
* Returns the attribute value in the given column for the given record.
*
* @param identifier The object identifier, usually the primary key value.
* @param columnName The column name of the attribute to search.
* @return The attribute value.
* @throws SQLException if an SQL operation failed.
*/
public Object getObject(final String identifier, final String columnName) throws SQLException {
return getResultSet(identifier).getObject(columnName);
}
/**
* Closes this statement and free all resources. After this method
* has been invoked, this object can't be used anymore.
*
* @throws SQLException If an error occurred while closing the statement.
*/
@Override
public void close() throws SQLException {
if (results != null) {
closeResultSet();
}
super.close();
}
}