/* * 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.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.SQLException; import java.util.Collection; import org.geotoolkit.resources.Errors; import org.apache.sis.util.Classes; /** * The handler for metadata proxy that implement (indirectly) metadata interfaces like * {@link org.opengis.metadata.Metadata}, {@link org.opengis.metadata.citation.Citation}, * <i>etc</i>. * * Any call to a method in a metadata interface is redirected toward the {@link #invoke} method. * This method uses reflection in order to find the caller's method and class name. The class * name is translated into a table name, and the method name is translated into a column name. * Then the information is fetch in the underlying metadata database. * * @author Touraïvane (IRD) * @author Martin Desruisseaux (IRD) * @version 3.03 * * @since 3.03 (derived from 2.1) * @module */ final class MetadataHandler implements InvocationHandler { /** * The identifier used in order to locate the record for * this metadata entity in the database. This is usually * the primary key in the table which contains this entity. */ private final String identifier; /** * The connection to the database. All metadata handlers * created from a single database should share the same source. */ private final MetadataSource source; /** * Creates a new metadata handler. * * @param identifier The identifier used in order to locate the record for * this metadata entity in the database. This is usually * the primary key in the table which contains this entity. * @param source The connection to the table which contains this entity. */ public MetadataHandler(final String identifier, final MetadataSource source) { this.identifier = identifier; this.source = source; } /** * Ensures that the given argument array has the expected length. */ private static void checkArgumentCount(final Object[] args, final int expected) { final int count = (args != null) ? args.length : 0; if (count != expected) { final short key; final Object value; if (count == 0) { key = Errors.Keys.NoParameter_1; value = "arg"; } else { key = Errors.Keys.UnexpectedParameter_1; value = args[0]; } throw new MetadataException(Errors.format(key, value)); } } /** * Invoked when any method from a metadata interface is invoked. * * @param proxy The object on which the method is invoked. * @param method The method invoked. * @param args The argument given to the method. */ @Override public Object invoke(final Object proxy, final Method method, final Object[] args) { final Class<?> type = method.getDeclaringClass(); if (source.standard.isMetadata(type)) { checkArgumentCount(args, 0); /* * The method invoked is a method from the metadata interface. Consequently, * the information should exists in the underlying database. */ try { return source.getValue(type, method, identifier); } catch (SQLException e) { Class<?> rt = method.getReturnType(); if (Collection.class.isAssignableFrom(rt)) { final Class<?> elementType = Classes.boundOfParameterizedProperty(method); if (elementType != null) { rt = elementType; } } throw new MetadataException(Errors.format(Errors.Keys.DatabaseFailure_2, rt, identifier), e); } } /* * The method invoked is a method inherited from a parent class, like Object.toString() * or Object.hashCode(). This information is not expected to exists in the database. * Do not delegate to the proxy since this result in never-ending loop. */ final String name = method.getName(); switch (name.hashCode()) { case -1618432855: { if (name.equals("identifier")) { checkArgumentCount(args, 1); return (args[0] == source) ? identifier : null; } break; } case -1295482945: { if (name.equals("equals")) { checkArgumentCount(args, 1); return proxy == args[0]; } break; } case -1776922004: { if (name.equals("toString")) { checkArgumentCount(args, 0); return toString(type); } break; } case 147696667: { if (name.equals("hashCode")) { checkArgumentCount(args, 0); return System.identityHashCode(proxy); } break; } } throw new MetadataException(Errors.format(Errors.Keys.IllegalInstruction_1, name)); } /** * Returns a string representation of a metadata of the given type. */ private String toString(final Class<?> type) { return Classes.getShortName(type) + "[id=\"" + identifier + "\"]"; } /** * Returns a string representation of this handler. * This is mostly for debugging purpose. */ @Override public String toString() { return toString(getClass()); } /** * Compares this method handler with the given object for equality. * Note that the same handler could be used for different metadata * objects if they share the same identifier. The {@code equals} * method is defined in order to allow to put {@code MetadataHandler} * in a hash map for this purpose. */ @Override public boolean equals(final Object other) { if (other instanceof MetadataHandler) { final MetadataHandler that = (MetadataHandler) other; return source == that.source && identifier.equals(that.identifier); } return false; } /** * Returns a hash code value for this method handler. */ @Override public int hashCode() { return identifier.hashCode(); } }