/* * This file is part of Alida, a Java library for * Advanced Library for Integrated Development of Data Analysis Applications. * * Copyright (C) 2010 - @YEAR@ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Fore more information on Alida, visit * * http://www.informatik.uni-halle.de/alida/ * */ /* * Most recent change(s): * * $Rev: 6309 $ * $Date: 2012-11-23 17:19:53 +0100 (Fr, 23 Nov 2012) $ * $Author: moeller $ * */ package de.unihalle.informatik.Alida.dataconverter; import de.unihalle.informatik.Alida.annotations.ALDDataConverterProvider; import de.unihalle.informatik.Alida.annotations.indexing.SezPozAdapter; import de.unihalle.informatik.Alida.exceptions.ALDDataConverterManagerException; import de.unihalle.informatik.Alida.exceptions.ALDDataConverterException; import de.unihalle.informatik.Alida.exceptions.ALDDataIOManagerException; import de.unihalle.informatik.Alida.exceptions.ALDProviderManagerException.ALDProviderManagerExceptionType; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import net.java.sezpoz.Index; import net.java.sezpoz.IndexItem; /** * This class implements a provider manager for data conversion. * <p> * For data input, it essentially looks up the correct provider for GUI-based * execution using the method of its super class and invokes its method. * <p> * It does its work in collaboration with * {@link de.unihalle.informatik.Alida.dataconverter.ALDDataConverter}. * * @author posch * */ public class ALDDataConverterManager { private static boolean debug = false; /** * Hashtable containing mappings of class pairs to a collection of provider class names. */ protected HashMap<String, Collection<String>> mapTable = null; /** * The singleton instance of this class. */ static final ALDDataConverterManager instance; static { instance = new ALDDataConverterManager(); } /** * Private constructor which initializes the provider map. * @throws ALDDataConverterManagerException */ private ALDDataConverterManager() { this.mapTable = initMapTable(); } /** * Return the single instance of this class * @return Single instance. */ public static ALDDataConverterManager getInstance() { return instance; } /** * Convert the <code>sourceObject</code> into an object of class * <code>targetClass</code>. * * @param sourceObject * @param sourceTypes * @param targetClass * @param targetTypes * @return * @throws ALDDataConverterException * @throws ALDDataConverterManagerException */ public Object convert( Object sourceObject, Type[] sourceTypes, Class<?> targetClass, Type[] targetTypes) throws ALDDataConverterException, ALDDataConverterManagerException { ALDDataConverter provider = this.getProvider(sourceObject.getClass(), sourceTypes, targetClass, targetTypes); return provider.convert( sourceObject, sourceTypes, targetClass, targetTypes); } /** * Convert the <code>sourceObject</code> into an object of class * <code>targetClass</code>. * * @param sourceObject * @param sourceTypes * @param targetClass * @param targetTypes * @return * @throws ALDDataConverterManagerException * @throws ALDDataConverterException */ public Object convert( ALDDataConverter provider, Object sourceObject, Field sourceField, Class<?> targetClass, Field targetField) throws ALDDataConverterManagerException, ALDDataConverterException { Type sourceType = sourceField.getGenericType(); Type[] sourceTypes = null; if (sourceType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) sourceType; sourceTypes = pt.getActualTypeArguments(); } Type[] targetTypes = null; Type targetType = targetField.getGenericType(); if (targetType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) targetType; targetTypes = pt.getActualTypeArguments(); } return provider.convert( sourceObject, sourceTypes, targetClass, targetTypes); } public Object convert( Object sourceObject, Field sourceField, Class<?> targetClass, Field targetField) throws ALDDataConverterManagerException, ALDDataConverterException { Type sourceType = sourceField.getGenericType(); Type[] sourceTypes = null; if (sourceType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) sourceType; sourceTypes = pt.getActualTypeArguments(); } Type[] targetTypes = null; Type targetType = targetField.getGenericType(); if (targetType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) targetType; targetTypes = pt.getActualTypeArguments(); } return this.convert( sourceObject, sourceTypes, targetClass, targetTypes); } /** * Convert the <code>sourceObject</code> into an object of class * <code>targetClass</code>. * * @param sourceObject * @param targetClass * @return converted object * @throws ALDDataConverterManagerException * @throws ALDDataConverterException */ public Object convert( Object sourceObject, Class<?> targetClass) throws ALDDataConverterManagerException, ALDDataConverterException { return this.convert( sourceObject, (Type[])null, targetClass, (Type[])null); } /** * Method to return an instance of the data converter provider for given classes. * <p> * @param sourceClass * @param targetClass * @return Provider instance. * @throws ALDDataConverterManagerException * @throws ALDDataIOManagerException */ public ALDDataConverter getProvider( Class<?> sourceClass, Type[] sourceTypes, Class<?> targetClass, Type[] targetTypes) throws ALDDataConverterManagerException { //TODO modify to DataConverterManagerException or generic manager exception if ( debug ) { System.out.println("ALDDataConverterManager::getProvider sourceClass <" + sourceClass.getName() + "> targetClass <" + targetClass + ">"); } // first look up a provider name ALDSourceTargetClassPair sourceTargetPair = new ALDSourceTargetClassPair(sourceClass, targetClass); String key = sourceTargetPair.toString(); Collection<String> providerNames = mapTable.get( key); if ( debug ) { System.out.println(" for " + key + " found " + providerNames.size() + " providers"); for ( String providerName : providerNames) System.out.println(" provider " + providerName); } if ( providerNames != null ) { for ( String providerName : providerNames) { // try to instantiate an instance of the provider try { Class<?> providerClass = Class.forName( providerName); ALDDataConverter providerObj = (ALDDataConverter) providerClass.newInstance(); if ( providerObj.supportConversion(sourceClass, sourceTypes, targetClass, targetTypes)) { if ( debug ) System.out.println( "ALDDataConverterManager::getProvider found provider " + providerName); return (ALDDataConverter)providerObj; } } catch (InstantiationException e) { throw new ALDDataConverterManagerException( ALDProviderManagerExceptionType.PROVIDER_INSTANTIATION_ERROR, "ALDBatchInputManager: Provider instantiation failed!"); } catch (IllegalAccessException e) { throw new ALDDataConverterManagerException( ALDProviderManagerExceptionType.UNSPECIFIED_ERROR, "ALDBatchInputManager: Illegal access noticed!"); } catch (ClassNotFoundException e) { throw new ALDDataConverterManagerException( ALDProviderManagerExceptionType.PROVIDER_INSTANTIATION_ERROR, "ALDBatchInputManager: " + "Provider class not found, instantiation failed!"); } } } // no provider found or none of the provides can handle throw new ALDDataConverterManagerException( ALDProviderManagerExceptionType.NO_PROVIDER_FOUND, "ALDDataConverterManager: No provider for class " + sourceClass + " --> " + targetClass + " found!"); } public ALDDataConverter getProvider(Class<?> sourceClass, Field sourceField, Class<?> targetClass, Field targetField) throws ALDDataConverterManagerException { Type sourceType = sourceField.getGenericType(); Type[] sourceTypes = null; if (sourceType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) sourceType; sourceTypes = pt.getActualTypeArguments(); } Type[] targetTypes = null; Type targetType = targetField.getGenericType(); if (targetType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) targetType; targetTypes = pt.getActualTypeArguments(); } return this.getProvider(sourceClass, sourceTypes, targetClass, targetTypes); } /** * Method to initialize the hashmap which registers data conversion providers. * @throws ALDDataConverterManagerException */ @SuppressWarnings("unchecked") protected static HashMap<String, Collection<String>> initMapTable() { if ( debug ) { System.out.println("ALDDataConverterManager::initMapTable"); } // temporary containing mappings of class pairs to a collection of providers // to collect provider and subsequently sort HashMap<String, LinkedList<ALDDataConverter>> tmpMapTable = new HashMap<String, LinkedList<ALDDataConverter>>(); // map of provider names to its priority // required as we cannot get hold of the annotation besides sezpoz Index final HashMap<String,Integer> priorityMap = new HashMap<String, Integer>(); Index<ALDDataConverterProvider,ALDDataConverter> indexItems = SezPozAdapter.load(ALDDataConverterProvider.class, ALDDataConverter.class); for (final IndexItem<ALDDataConverterProvider,ALDDataConverter> item : indexItems ) { // class name of provider String className = item.className(); // and its priority int priority = item.annotation().priority(); if ( debug ) System.out.println( "found: " + className); Method providesMethod; ALDDataConverter provider = null; try { provider = (ALDDataConverter)(Class.forName( className).newInstance()); priorityMap.put(provider.getClass().getName(), priority); } catch (Exception e) { // do not throw an exception as we can still try to continue System.err.println( "ALDDataConverterManager::initMap cannot create an instance for " + className); continue; } Collection<ALDSourceTargetClassPair> sourceTargetPairs = null; try { Class<?> params[] = {}; Object paramsObj[] = {}; providesMethod = Class.forName(className).getDeclaredMethod( ALDDataConverter.providesMethodName, params); sourceTargetPairs = (Collection<ALDSourceTargetClassPair>)(providesMethod.invoke( provider, paramsObj)); } catch (Exception e) { // do not throw an exception as we can still try to continue System.err.println( "ALDDataConverterManager::initMap failed to invoke method " + ALDDataConverter.providesMethodName + " of converter provider " + className + " to get provided class pairs"); } if ( sourceTargetPairs != null ) { for ( ALDSourceTargetClassPair sourceTargetPair : sourceTargetPairs ) { String key = sourceTargetPair.toString(); if ( debug ) System.out.println( " supported class (priority = " + priority + "):" + sourceTargetPair.getSourceClass().getName() + " -> " + sourceTargetPair.getTargetClass().getName() + " key = " + key); LinkedList<ALDDataConverter> providers = tmpMapTable.get(key); if ( providers == null) providers = new LinkedList<ALDDataConverter>(); providers.add(provider); tmpMapTable.put( key, providers); } } } //TODO: sort each list of provider names according to priority HashMap<String, Collection<String>> mapTable = new HashMap<String, Collection<String>>(); for ( String sourceTargetPair : tmpMapTable.keySet()) { LinkedList<ALDDataConverter> providers = tmpMapTable.get(sourceTargetPair); java.util.Collections.sort(providers, new Comparator<ALDDataConverter>() { @Override public int compare(ALDDataConverter o1, ALDDataConverter o2) { int p1 = priorityMap.get(o1.getClass().getName()); int p2 = priorityMap.get(o2.getClass().getName()); return p2 - p1; } }); LinkedList<String> providerNames = new LinkedList<String>(); for ( ALDDataConverter provider : providers) { providerNames.add( provider.getClass().getName()); } mapTable.put( sourceTargetPair, providerNames); } if ( debug ) printMap(mapTable); return mapTable; } public static void printMap(HashMap<String, Collection<String>> mapTable) { System.out.println("Map of data converter providers"); for ( String sourceTargetPair : mapTable.keySet()) { System.out.println(" " + sourceTargetPair); for ( String providerName : mapTable.get(sourceTargetPair)) { System.out.println( " " + providerName); } } } // ==================================================================================== // local class /** * A pair of source and target pair (of a converter) * * @author posch * */ public static class ALDSourceTargetClassPair { public ALDSourceTargetClassPair(Class<?> sourceClass, Class<?> targetClass) { this.sourceClass = sourceClass; this.targetClass = targetClass; } private Class<?> sourceClass; private Class<?> targetClass; @Override public boolean equals( Object obj) { if ( obj instanceof ALDSourceTargetClassPair ) { ALDSourceTargetClassPair pair = (ALDSourceTargetClassPair)obj; return ( this.getSourceClass() == pair.getSourceClass() && this.getTargetClass() == pair.getTargetClass()); } else { return false; } } @Override public String toString() { return new String( sourceClass.getName() + ";" + targetClass.getName()); } /** * @return the sourceClass */ public Class<?> getSourceClass() { return sourceClass; } /** * @param sourceClass the sourceClass to set * @return */ protected void setSourceClass(Class<?> sourceClass) { this.sourceClass = sourceClass; } /** * @return the targetClass */ public Class<?> getTargetClass() { return targetClass; } /** * @param targetClass the targetClass to set */ protected void setTargetClass(Class<?> targetClass) { this.targetClass = targetClass; } } }