/*
* RapidMiner
*
* Copyright (C) 2001-2014 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* 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 http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.metadata;
import java.awt.Component;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.rapidminer.gui.flow.ExampleSetMetaDataTableModel;
import com.rapidminer.operator.ports.metadata.ExampleSetMetaData;
import com.rapidminer.operator.ports.metadata.MetaData;
/** Subclasses of {@link MetaDataRendererFactory} can register themselves here.
*
* @author Simon Fischer, Gabor Makrai
*
*/
public class MetaDataRendererFactoryRegistry {
private Map<Class<? extends MetaData>,MetaDataRendererFactory> factories = new HashMap<Class<? extends MetaData>, MetaDataRendererFactory>();
private static final MetaDataRendererFactoryRegistry INSTANCE = new MetaDataRendererFactoryRegistry();
static {
getInstance().register(new MetaDataRendererFactory() {
@Override
public Class<? extends MetaData> getSupportedClass() {
return ExampleSetMetaData.class;
}
@Override
public Component createRenderer(MetaData metaData) {
return ExampleSetMetaDataTableModel.makeTableForToolTip((ExampleSetMetaData) metaData);
}
});
}
/** Gets the singleton instance. */
public static MetaDataRendererFactoryRegistry getInstance() {
return INSTANCE;
}
/** Registers a new factory. */
public void register(MetaDataRendererFactory factory) {
factories.put(factory.getSupportedClass(), factory);
}
private int getInheritenceLevelDistanceRecursive(Class<?> currentClass, Class<?> targetClass, int distance) {
// if the current class is the target class then return with the current distance
if (currentClass.equals(targetClass)) {
return distance;
}
// if it is a leaf node of the inheritance tree and it is not the target class then return with -1
if (currentClass.isInterface() && currentClass.getInterfaces().length == 0) {
return -1;
}
if (!currentClass.isInterface() && currentClass.getSuperclass().equals(Object.class)) {
return -1;
}
// determine all super* (included superclass and superinterfaces)
Class<?>[] superClassAndInterfaces = null;
if (currentClass.isInterface()) {
// if it is interface then there is no superclass
superClassAndInterfaces = new Class<?>[currentClass.getInterfaces().length];
} else {
// if it is a class then then is a superclass
superClassAndInterfaces = new Class<?>[currentClass.getInterfaces().length + 1];
}
// add interfaces to list
for (int i=0;i<currentClass.getInterfaces().length;i++) {
superClassAndInterfaces[i] = currentClass.getInterfaces()[i];
}
// add superclass if it is not interface
if (!currentClass.isInterface()) {
superClassAndInterfaces[superClassAndInterfaces.length - 1] = currentClass.getSuperclass();
}
// run recursive search
int[] returnDistances = new int[superClassAndInterfaces.length];
for (int i=0;i<superClassAndInterfaces.length;i++) {
returnDistances[i] = getInheritenceLevelDistanceRecursive(superClassAndInterfaces[i], targetClass, distance + 1);
}
// select the smallest valid value from the return list
int minReturn = Integer.MAX_VALUE;
for (int i=0;i<returnDistances.length;i++) {
if (returnDistances[i] != -1 && returnDistances[i] < minReturn) {
minReturn = returnDistances[i];
}
}
// if the minReturn is not changed then return -1
// this means that target class is nnot found in that branch
if (minReturn == Integer.MAX_VALUE) {
return -1;
} else {
return minReturn;
}
}
/**
*
* Determine the inheritance distance between two classes
*
* @param child Child class
* @param parent Parent class
* @return Distance between the child and the parent
*/
private int getInheritenceLevelDistance(Class<?> child, Class<?> parent) {
// null input parameters are not acceptable
if (child == null || parent == null) {
return -1;
}
// if they are not in the same inheritance branch then return -1
if (!parent.isAssignableFrom(child)) {
return -1;
} else {
// call the recursive inheritance tree travelsar
return getInheritenceLevelDistanceRecursive(child, parent, 0);
}
}
/** Creates a renderer for this meta data object or null if there is no suitable renderer
* or if the meta data is null. */
public Component createRenderer(MetaData metaData) {
// handle the case when we get null metadata
if (metaData == null) {
return null;
}
// first of all, we need to check that factories contains or doesn't contain render for metadata
if (factories.containsKey(metaData.getClass())) {
// if there is a renderer factory element in factories then we need to check that it is null or not
MetaDataRendererFactory factory = factories.get(metaData.getClass());
if (factory == null) {
// if it is null then return with null
return null;
} else {
// it it is not null then call the createRenderer function on the renderer factory
return factory.createRenderer(metaData);
}
} else { // there is no factory in factories for the given metadata, so let's try to find one
// find the closest (inheritance) renderer from the factories
int distance = Integer.MAX_VALUE;
Iterator<Class<? extends MetaData>> iterator = factories.keySet().iterator();
Class<?> rendererCandidateMetaDataClass = null;
while (iterator.hasNext()) {
// get the next key from the factories
Class<?> metaDataClass = iterator.next();
// calculate the distance between key MD and parameter MD
int currentDistance = getInheritenceLevelDistance(metaData.getClass(), metaDataClass);
// find the closest one
if (currentDistance != -1 && currentDistance < distance) {
distance = currentDistance;
rendererCandidateMetaDataClass = metaDataClass;
}
}
// if an appropriate renderer is exist, then register it for this metadata
if (rendererCandidateMetaDataClass != null) {
MetaDataRendererFactory factory = factories.get(rendererCandidateMetaDataClass);
factories.put(metaData.getClass(), factory);
return factory.createRenderer(metaData);
} else { // else add null to this metadata (to avoid further lookups)
factories.put(metaData.getClass(), null);
return null;
}
}
}
}