/** * Copyright (C) 2001-2017 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.Arrays; 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; } boolean isInterface = currentClass.isInterface(); Class<?>[] interfaces = currentClass.getInterfaces(); // if it is a leaf node of the inheritance tree and it is not the target class then return // with -1 if (isInterface && interfaces.length == 0) { return -1; } if (!isInterface && currentClass.getSuperclass().equals(Object.class)) { return -1; } // determine all super* (included superclass and superinterfaces) // if it is interface then there is no superclass Class<?>[] superClassAndInterfaces = Arrays.copyOf(interfaces, interfaces.length + (isInterface ? 0 : 1)); // add superclass if it is not interface if (!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; } } } }