/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2005-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2007-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.internal.sql.table;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLNonTransientException;
import java.util.Locale;
import org.geotoolkit.resources.Errors;
import org.apache.sis.util.resources.IndexedResourceBundle;
import org.apache.sis.util.Classes;
/**
* Base class for exceptions that may occur while querying the catalog.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Rémi Eve (IRD)
* @version 3.09
*
* @since 3.09 (derived from Seagis)
* @module
*/
public class CatalogException extends SQLNonTransientException {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = 3838293108990270182L;
/**
* The table name where a problem occurred, or {@code null} if unknown.
*/
private String table;
/**
* The column name where a problem occurred, or {@code null} if unknown.
*/
private String column;
/**
* The primary key for the record where a problem occurred, or {@code null} if unknown.
*/
private Comparable<?> key;
/**
* The locale to use for formatting error messages, or {@code null} for the default.
*/
private Locale locale;
/**
* Creates an exception with no cause and no details message.
*/
public CatalogException() {
super();
}
/**
* Creates an exception with the specified details message.
*
* @param message The detail message.
*/
public CatalogException(final String message) {
super(message);
}
/**
* Creates an exception with the specified cause and no details message.
*
* @param cause The cause for this exception.
*/
public CatalogException(final Exception cause) {
super(cause);
}
/**
* Creates an exception with the specified details message and cause.
*
* @param message The detail message.
* @param cause The cause for this exception.
*/
public CatalogException(final String message, final Exception cause) {
super(message, cause);
}
/**
* Returns {@code true} if {@link #setMetadata} has been invoked with non-null values.
*/
final boolean isMetadataInitialized() {
return (table != null) || (column != null) || (key != null);
}
/**
* Sets the table and column names from the given column.
*
* @param column The column where a problem occurred.
* @param key The key value for the record where a problem occurred, or {@code null} if none.
* The key shall be either a {@link String} or {@link Integer} instance.
*/
final void setMetadata(final Column column, final Comparable<?> key) {
this.table = column.table;
this.column = column.name;
this.key = key;
}
/**
* Sets the table and column names from the {@linkplain ResultSetMetaData result set metadata}.
* <strong>Note that the result set will be closed</strong>, because this exception is always
* thrown when an error occurred while reading this result set.
*
* @param table The table that produced the result set, or {@code null} if unknown.
* @param results The result set in which a problem occurred, or {@code null} if none.
* @param column The column index where a problem occurred (number starts at 1), or {@code 0} if unknown.
* @param key The key value for the record where a problem occurred, or {@code null} if none.
* The key shall be either a {@link String} or {@link Integer} instance.
* @throws SQLException if the metadata can't be read from the result set.
*/
final void setMetadata(final Table table, final ResultSet results, final int column, final Comparable<?> key)
throws SQLException
{
boolean noTable=true, noColumn=true;
if (results != null && column != 0 && !results.isClosed()) {
final ResultSetMetaData metadata = results.getMetaData();
if (metadata != null) {
this.table = metadata.getTableName (column);
this.column = metadata.getColumnName(column);
noTable = (this.table == null) || (this.table = this.table .trim()).isEmpty();
noColumn = (this.column == null) || (this.column = this.column.trim()).isEmpty();
}
results.close();
}
/*
* We tried to use the database metadata in priority, on the assumption that they
* are closer to the SQL statement really executed (we could have a bug in the way
* we created our SQL statement). But some JDBC drivers don't provide information.
* In the later case, we fallback on the information found in our Column objects.
*/
if (table != null) {
locale = table.getLocale();
if (noTable || noColumn) {
final Column c = table.getColumn(column);
if (c != null) {
if (noTable) this.table = c.table;
if (noColumn) this.column = c.name;
}
}
}
this.key = key;
}
/**
* Clears the column name. Invoked when this name is not reliable.
*/
final void clearColumnName() {
column = null;
}
/**
* Returns the table name where a problem occurred, or {@code null} if unknown.
*
* @return The table where a problem occurred, or {@code null} if unknown.
*/
public String getTableName() {
return table;
}
/**
* Returns the column name where a problem occurred, or {@code null} if unknown.
*
* @return The column where a problem occurred, or {@code null} if unknown.
*/
public String getColumnName() {
return column;
}
/**
* Returns the primary key for the record where a problem occurred, or {@code null} if unknown.
* The primary key is either a {@link String} or an {@link Integer} instance.
*
* @return The identifier of the entry where a problem occurred, or {@code null} if unknown.
*/
public Comparable<?> getPrimaryKey() {
return key;
}
/**
* Returns the resources to use for formatting error messages.
*/
final IndexedResourceBundle errors() {
return Errors.getResources(locale);
}
/**
* Returns a concatenation of the {@linkplain #getMessage details message} and the table
* and column name where the error occurred.
*/
@Override
public String getLocalizedMessage() {
String message = super.getLocalizedMessage();
if (message == null) {
final Throwable cause = getCause();
if (cause != null) {
message = cause.getLocalizedMessage();
if (message == null) {
message = Classes.getShortClassName(cause);
}
}
}
final String table = getTableName();
final String column = getColumnName();
final Comparable<?> key = getPrimaryKey();
if (table != null) {
final short localKey;
final Comparable<?>[] args;
if (column != null) {
if (key != null) {
localKey = Errors.Keys.CantReadDatabaseRecord_3;
args = new Comparable<?>[] {table, column, key};
} else {
localKey = Errors.Keys.CantReadDatabaseTable_2;
args = new String[] {table, column};
}
} else {
if (key != null) {
localKey = Errors.Keys.CantReadDatabaseRecord_2;
args = new Comparable<?>[] {table, key};
} else {
localKey = Errors.Keys.CantReadDatabaseTable_1;
args = new String[] {table};
}
}
final String explain = errors().getString(localKey, args);
if (message != null) {
message = explain + ' ' + message;
}
}
return message;
}
}