/* * Copyright 2013 The Solmix Project * * This 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; either version 2.1 of * the License, or (at your option) any later version. * * This software 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. * * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.gnu.org/licenses/ * or see the FSF site: http://www.fsf.org. */ package org.solmix.runtime.adapter.support; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.solmix.commons.util.Assert; import org.solmix.runtime.Container; import org.solmix.runtime.ContainerExtension; import org.solmix.runtime.adapter.AdapterFactory; import org.solmix.runtime.adapter.AdapterManager; /** * * @author solmix.f@gmail.com * @version $Id$ 2014年4月24日 */ public class AdapterManagerImpl implements AdapterManager, ContainerExtension { private Map<String, Map<String, AdapterFactory>> factoryCache; private Map<Class<?>, Class<?>[]> classSearchCache; final Map<String,List<AdapterFactory>> factories; private Container container; protected final Logger LOG = LoggerFactory.getLogger(this.getClass().getName()); public AdapterManagerImpl(){ factories=new HashMap<String,List<AdapterFactory>>(); } public AdapterManagerImpl(final Container systemContext) { this(); setContainer(systemContext); } /** * {@inheritDoc} * * @see org.solmix.runtime.adapter.AdapterManager#getAdapter(java.lang.Object, * java.lang.Class) */ @Override public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) { Assert.isNotNull(adaptable); Assert.isNotNull(type); Map<String, AdapterFactory> factories = getAdapterFactories(adaptable.getClass()); AdapterFactory factory = factories.get(type.getName()); AdapterType result = null; if (factory != null) { if (LOG.isTraceEnabled()) { LOG.trace("Using adapter factory " + factory + " to map " + adaptable + " to " + type); } result = factory.getAdapter(adaptable, type); } return result; } /** * @param class1 * @return */ private Map<String, AdapterFactory> getAdapterFactories(Class<?> clazz) { Map<String, Map<String, AdapterFactory>> cache = factoryCache; if (cache == null) factoryCache = cache = Collections.synchronizedMap(new HashMap<String, Map<String, AdapterFactory>>( 30)); Map<String, AdapterFactory> table = cache.get(clazz.getName()); if (table != null) { table = new HashMap<String, AdapterFactory>(4); Class<?>[] classes = computeClassOrder(clazz); for (int i = 0; i < classes.length; i++) addFactoriesForClass(classes[i].getName(), table); // cache the table cache.put(clazz.getName(), table); } return table; } private void addFactoriesForClass(String typeName, Map<String, AdapterFactory> table) { List<AdapterFactory> factoryList = getFactories().get(typeName); if (factoryList == null) return; for (int i = 0, imax = factoryList.size(); i < imax; i++) { AdapterFactory factory = factoryList.get(i); Class<?>[] adapters = factory.getAdapterList(); for (int j = 0; j < adapters.length; j++) { String adapterName = adapters[j].getName(); if (table.get(adapterName) == null) table.put(adapterName, factory); } } } /** * @return */ protected Map<String, List< AdapterFactory>> getFactories() { return factories; } /** * @param clazz * @return */ private Class<?>[] computeClassOrder(Class<?> clazz) { Class<?>[] classes = null; Map<Class<?>, Class<?>[]> cache = classSearchCache; if (classSearchCache == null) classSearchCache = cache = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>[]>()); else classes = cache.get(clazz); if (classes == null) { classes = doComputeClassOrder(clazz); cache.put(clazz, classes); } return classes; } private Class<?>[] doComputeClassOrder(Class<?> adaptable) { List<Class<?>> classes = new ArrayList<Class<?>>(); Class<?> clazz = adaptable; Set<Class<?>> seen = new HashSet<Class<?>>(4); // first traverse class hierarchy while (clazz != null) { classes.add(clazz); clazz = clazz.getSuperclass(); } // now traverse interface hierarchy for each class Class<?>[] classHierarchy = classes.toArray(new Class[classes.size()]); for (int i = 0; i < classHierarchy.length; i++) computeInterfaceOrder(classHierarchy[i].getInterfaces(), classes, seen); return classes.toArray(new Class[classes.size()]); } private void computeInterfaceOrder(Class<?>[] interfaces, Collection<Class<?>> classes, Set<Class<?>> seen) { List<Class<?>> newInterfaces = new ArrayList<Class<?>>( interfaces.length); for (int i = 0; i < interfaces.length; i++) { Class<?> interfac = interfaces[i]; if (seen.add(interfac)) { // note we cannot recurse here without changing the resulting // interface order classes.add(interfac); newInterfaces.add(interfac); } } for (Iterator<Class<?>> it = newInterfaces.iterator(); it.hasNext();) computeInterfaceOrder(it.next().getInterfaces(), classes, seen); } /** * {@inheritDoc} * * @see org.solmix.runtime.adapter.AdapterManager#hasAdapter(java.lang.Object, * java.lang.String) */ @Override public boolean hasAdapter(Object adaptable, String adapterTypeName) { return getAdapterFactories(adaptable.getClass()).get(adapterTypeName)!=null; } /** * {@inheritDoc} * * @see org.solmix.runtime.adapter.AdapterManager#registerAdapters(org.solmix.runtime.adapter.AdapterFactory, * java.lang.Class) */ @Override public void registerAdapters(AdapterFactory factory, Class<?> adaptable) { List<AdapterFactory> list = factories.get(adaptable.getName()); if (list == null) { list = new ArrayList<AdapterFactory>(5); factories.put(adaptable.getName(), list); } list.add(factory); flush(); } /** * {@inheritDoc} * * @see org.solmix.runtime.adapter.AdapterManager#unregisterAdapters(org.solmix.runtime.adapter.AdapterFactory) */ @Override public void unregisterAdapters(AdapterFactory factory) { for (Iterator<List<AdapterFactory>> it = factories.values().iterator(); it.hasNext();) it.next().remove(factory); flush(); } public synchronized void flush() { this.factoryCache=null; this.classSearchCache=null; } /** * {@inheritDoc} * * @see org.solmix.runtime.adapter.AdapterManager#unregisterAdapters(org.solmix.runtime.adapter.AdapterFactory, * java.lang.Class) */ @Override public void unregisterAdapters(AdapterFactory factory, Class<?> adaptable) { List<AdapterFactory> lf = factories.get(adaptable.getName()); if (lf != null) { lf.remove(factory); } flush(); } /** * {@inheritDoc} * * @see org.solmix.runtime.ContainerExtension#setContainer(org.solmix.runtime.Container) */ @Override public void setContainer(Container container) { this.container=container; container.setExtension(this,AdapterManager.class); } protected Container getContainer(){ return container; } }