package de.lmu.ifi.dbs.elki.algorithm; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures Copyright (C) 2011 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import de.lmu.ifi.dbs.elki.data.type.TypeInformation; import de.lmu.ifi.dbs.elki.database.Database; import de.lmu.ifi.dbs.elki.database.relation.Relation; import de.lmu.ifi.dbs.elki.distance.distancefunction.DistanceFunction; import de.lmu.ifi.dbs.elki.logging.Logging; import de.lmu.ifi.dbs.elki.result.Result; import de.lmu.ifi.dbs.elki.utilities.exceptions.APIViolationException; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ObjectParameter; /** *

* This class serves also as a model of implementing an algorithm within this * framework. Any Algorithm that makes use of these flags may extend this class. *

* * @author Arthur Zimek * * @apiviz.landmark * @apiviz.excludeSubtypes * * @param the result type */ public abstract class AbstractAlgorithm implements Algorithm { /** * Constructor. */ protected AbstractAlgorithm() { super(); } @SuppressWarnings("unchecked") @Override public R run(Database database) { final Object[] relations1; final Class[] signature1; final Object[] relations2; final Class[] signature2; // Build candidate method signatures { final TypeInformation[] inputs = getInputTypeRestriction(); relations1 = new Object[inputs.length + 1]; signature1 = new Class[inputs.length + 1]; relations2 = new Object[inputs.length]; signature2 = new Class[inputs.length]; // First parameter is the database relations1[0] = database; signature1[0] = Database.class; // Other parameters are the bound relations for(int i = 0; i < inputs.length; i++) { // TODO: don't bind the same relation twice? // But sometimes this is wanted (e.g. using projected distances) relations1[i + 1] = database.getRelation(inputs[i]); signature1[i + 1] = Relation.class; relations2[i] = database.getRelation(inputs[i]); signature2[i] = Relation.class; } } // Find appropriate run method. Method runmethod1 = null; Method runmethod2 = null; try { runmethod1 = this.getClass().getMethod("run", signature1); runmethod2 = null; } catch(SecurityException e) { throw new APIViolationException("Security exception finding an appropriate 'run' method.", e); } catch(NoSuchMethodException e) { runmethod1 = null; // Try without "database" parameter. try { runmethod2 = this.getClass().getMethod("run", signature2); } catch(NoSuchMethodException e2) { runmethod2 = null; } catch(SecurityException e2) { throw new APIViolationException("Security exception finding an appropriate 'run' method.", e2); } } if(runmethod1 != null) { try { StringBuffer buf = new StringBuffer(); for(Class cls : signature1) { buf.append(cls.toString()).append(","); } return (R) runmethod1.invoke(this, relations1); } catch(IllegalArgumentException e) { throw new APIViolationException("Invoking the real 'run' method failed.", e); } catch(IllegalAccessException e) { throw new APIViolationException("Invoking the real 'run' method failed.", e); } catch(InvocationTargetException e) { if(e.getTargetException() instanceof RuntimeException) { throw (RuntimeException) e.getTargetException(); } throw new APIViolationException("Invoking the real 'run' method failed: " + e.getTargetException().toString(), e.getTargetException()); } } else if(runmethod2 != null) { try { StringBuffer buf = new StringBuffer(); for(Class cls : signature1) { buf.append(cls.toString()).append(","); } return (R) runmethod2.invoke(this, relations2); } catch(IllegalArgumentException e) { throw new APIViolationException("Invoking the real 'run' method failed.", e); } catch(IllegalAccessException e) { throw new APIViolationException("Invoking the real 'run' method failed.", e); } catch(InvocationTargetException e) { if(e.getTargetException() instanceof RuntimeException) { throw (RuntimeException) e.getTargetException(); } throw new APIViolationException("Invoking the real 'run' method failed: " + e.getTargetException().toString(), e.getTargetException()); } } else { throw new APIViolationException("No appropriate 'run' method found."); } } /** * Get the input type restriction used for negotiating the data query. * * @return Type restriction */ @Override public abstract TypeInformation[] getInputTypeRestriction(); /** * Get the (STATIC) logger for this class. * * @return the static logger */ abstract protected Logging getLogger(); /** * Make a default distance function configuration option * * @param Distance function type * @param defaultDistanceFunction Default value * @param restriction Restriction class * @return Parameter object */ public static > ObjectParameter makeParameterDistanceFunction(Class defaultDistanceFunction, Class restriction) { return new ObjectParameter(AbstractDistanceBasedAlgorithm.DISTANCE_FUNCTION_ID, restriction, defaultDistanceFunction); } }