All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
it.tidalwave.openrdf.elmo.impl.ElmoWrapper Maven / Gradle / Ivy
/***********************************************************************************************************************
*
* blueMarine Semantic - open source media workflow
* Copyright (C) 2008-2011 by Tidalwave s.a.s. (http://www.tidalwave.it)
*
***********************************************************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
***********************************************************************************************************************
*
* WWW: http://bluemarine.tidalwave.it
* SCM: https://kenai.com/hg/bluemarine~semantic-src
*
**********************************************************************************************************************/
package it.tidalwave.openrdf.elmo.impl;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import org.openrdf.elmo.Entity;
import it.tidalwave.util.logging.Logger;
import it.tidalwave.openrdf.elmo.ElmoUtils;
import it.tidalwave.semantic.Wrapper.WrapperSpi;
/*******************************************************************************
*
* @author Fabrizio Giudici
* @version $Id: ElmoWrapper.java,v 4fbb06e9364c 2011/03/06 21:25:16 fabrizio $
*
******************************************************************************/
@ThreadSafe
public class ElmoWrapper implements WrapperSpi
{
private static final String CLASS = ElmoWrapper.class.getName();
private static final Logger logger = Logger.getLogger(CLASS);
private final Set> interfaces = new HashSet>();
private final Map, Class>> implementationMap = new HashMap, Class>>();
// // FIXME: there should be one per transaction
// private final static IdentityWeakHashMap FLYWEIGHT_CACHE =
// new IdentityWeakHashMap();
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Override
public void register (final @Nonnull Class> interfaceClass,
final @Nonnull Class> implementationClass)
{
interfaces.add(interfaceClass);
implementationMap.put(interfaceClass, implementationClass);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Override
public boolean isRegistered (final @Nonnull Class> clazz)
{
for (final Class> interfaceClass : interfaces)
{
logger.finer(">>>> probing %s", interfaceClass);
if (interfaceClass.isAssignableFrom(clazz))
{
return true;
}
}
return false;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Override
@CheckForNull
public Class extends T> findImplementation (final @Nonnull Class clazz)
{
for (final Entry, Class>> entrySet : implementationMap.entrySet())
{
final Class> interfaceClass = entrySet.getKey();
if (clazz.isAssignableFrom(interfaceClass))
{
return (Class extends T>)entrySet.getValue();
}
}
return null;
// return implementationMap.get(clazz);
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Override
@CheckForNull
public X wrap (final @Nullable X object)
{
if (logger.isLoggable(Level.FINEST)) // safeToString() is expensive
{
logger.finest("wrap(%s)", ElmoUtils.safeToString(object));
}
X result = object;
if ((object != null) && !(object instanceof ProxyMarker))
{
// logger.finest(">>>> FLYWEIGHT_CACHE is: %s", FLYWEIGHT_CACHE);
//
// if (FLYWEIGHT_CACHE.containsKey(object))
// {
// result = (X)FLYWEIGHT_CACHE.get(object);
// logger.finest(">>>> wrapper already in flyweight cache: %s", result);
// }
// else
// {
result = (object instanceof Collection>) ? (X)wrapCollection((Collection)object)
: (X)wrapSingle(object);
//
// if (object != result)
// {
// FLYWEIGHT_CACHE.put(object, (ProxyMarker)result);
// }
// }
}
if (logger.isLoggable(Level.FINEST)) // safeToString() is expensive
{
logger.finest(">>>> returning %s", ElmoUtils.safeToString(result));
// logger.finest(">>>> on exit FLYWEIGHT_CACHE is: %s", FLYWEIGHT_CACHE);
}
return result;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Override
@CheckForNull
public X unwrap (@CheckForNull final X object)
{
if (logger.isLoggable(Level.FINEST))
{
logger.finest("unwrap(%s)", ElmoUtils.safeToString(object));
}
X result = object;
if (object != null)
{
if (object instanceof Collection>) // this test FIRST since Collections aren't instanceof ProxyMarker
{
result = (X)unwrapCollection((Collection)object);
}
if (object.getClass().isArray())
{
result = (X)unwrapArray((Object[])object);
}
if (object instanceof ProxyMarker)
{
result = (X)((ProxyMarker)object).getDelegate();
}
}
if (logger.isLoggable(Level.FINEST))
{
logger.finest(">>>> returning %s", ElmoUtils.safeToString(result));
}
return result;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Override
public boolean isWrapped (final @Nullable Object object)
{
return object instanceof ProxyMarker;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
private Object wrapSingle (final @Nonnull Object object)
{
if (logger.isLoggable(Level.FINEST)) // safeToString() is expensive
{
logger.finest("wrapSingle(%s)", ElmoUtils.safeToString(object));
}
if (object instanceof ProxyMarker)
{
Thread.dumpStack();
throw new AssertionError("trying to wrap a wrapped object");
}
Object result = object;
final List> proxiedInterfaces = new ArrayList>();
// FIXME: can we just put the list of object.getClass().getInterfaces()?
// Or is there any interface we don't want to expose?
// The test is still important since we only wrap registered entities,
// not all objects.
for (final Class> interfaceClass : interfaces)
{
// if (logger.isLoggable(Level.FINEST))
// {
// logger.finest(">>>> probing %s", interfaceClass);
// }
//
if (interfaceClass.isAssignableFrom(object.getClass()))
{
proxiedInterfaces.add(interfaceClass);
}
}
if (!proxiedInterfaces.isEmpty())
{
proxiedInterfaces.add(Entity.class); // FIXME: why expose it? Can be dropped?
proxiedInterfaces.add(ProxyMarker.class);
logger.finer(">>>> wrapping, proxied interfaces: %s", proxiedInterfaces);
// final Class> implementationClass =
// (proxiedInterfaces.size() == 1) ? implementationMap.get(proxiedInterfaces.get(0)) : Object.class;
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
result = Proxy.newProxyInstance(classLoader, proxiedInterfaces.toArray(new Class>[0]),
new DelegateInvocationHandler((Entity)object));
}
if (logger.isLoggable(Level.FINEST)) // safeToString() is expensive
{
logger.finest(">>>> returning %s", ElmoUtils.safeToString(result));
}
return result;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
private Collection wrapCollection (final @Nonnull Collection collection)
{
// We could use the trick of copying collection into a temp, clearing
// collection and putting results into it. In this way we would be sure
// that the result if of the same type of the argument. Unfortunately,
// we would get some decorated collections from Elmo that would create
// performance issues when manipulated.
Collection result = null;
if (collection instanceof ArrayList>)
{
result = new ArrayList();
}
else if (collection instanceof HashSet>)
{
result = new HashSet();
}
else if (collection instanceof TreeSet>)
{
result = new TreeSet();
}
// FIXME: e.g. a java.util.Collections$UnmodifiableRandomAccessList
// But in this case you should wrap the result with an UnmodifiableXXX
else if (collection instanceof Collection>)
{
result = new ArrayList();
}
else
{
logger.warning("Cannot handle: " + collection.getClass());
return collection;
}
for (final Object entity : collection)
{
result.add(wrap(entity));
}
return result;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
private Collection unwrapCollection (final @Nonnull Collection collection)
{
// We could use the trick of copying collection into a temp, clearing
// collection and putting results into it. In this way we would be sure
// that the result if of the same type of the argument. Unfortunately,
// we would get some decorated collections from Elmo that would create
// performance issues when manipulated.
Collection result = null;
if (collection instanceof ArrayList>)
{
result = new ArrayList();
}
else if (collection instanceof HashSet>)
{
result = new HashSet();
}
else if (collection instanceof TreeSet>)
{
result = new TreeSet();
}
else
{
logger.warning("Cannot handle: " + collection.getClass());
return collection;
}
for (final Object entity : collection)
{
result.add(unwrap(entity));
}
return result;
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
private Object[] unwrapArray (final @Nonnull Object[] array)
{
if (array == null)
{
return null;
}
final Object[] result = new Object[array.length];
if (array != null)
{
System.arraycopy(array, 0, result, 0, array.length);
for (int i = 0; i < array.length; i++)
{
if (array[i] instanceof ProxyMarker)
{
result[i] = unwrap(array[i]);
}
}
}
return result;
}
}