/*
* Copyright 2014 Daniel Bechler
*
* 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.
*/
package de.danielbechler.diff.introspection;
import de.danielbechler.diff.ObjectDifferBuilder;
import de.danielbechler.diff.instantiation.InstanceFactory;
import de.danielbechler.diff.instantiation.PublicNoArgsConstructorInstanceFactory;
import de.danielbechler.diff.instantiation.TypeInfo;
import de.danielbechler.diff.node.DiffNode;
import de.danielbechler.diff.path.NodePath;
import de.danielbechler.diff.path.NodePathValueHolder;
import de.danielbechler.util.Assert;
import de.danielbechler.util.Classes;
import java.util.HashMap;
import java.util.Map;
/**
* @author Daniel Bechler
*/
public class IntrospectionService implements IntrospectionConfigurer, IsIntrospectableResolver, TypeInfoResolver, PropertyAccessExceptionHandlerResolver
{
private final Map<Class<?>, Introspector> typeIntrospectorMap = new HashMap<Class<?>, Introspector>();
private final Map<Class<?>, IntrospectionMode> typeIntrospectionModeMap = new HashMap<Class<?>, IntrospectionMode>();
private final NodePathValueHolder<Introspector> nodePathIntrospectorHolder = new NodePathValueHolder<Introspector>();
private final NodePathValueHolder<IntrospectionMode> nodePathIntrospectionModeHolder = new NodePathValueHolder<IntrospectionMode>();
private final ObjectDifferBuilder objectDifferBuilder;
private Introspector defaultIntrospector = new StandardIntrospector();
private InstanceFactory instanceFactory = new PublicNoArgsConstructorInstanceFactory();
private PropertyAccessExceptionHandler defaultPropertyAccessExceptionHandler = new DefaultPropertyAccessExceptionHandler();
public IntrospectionService(final ObjectDifferBuilder objectDifferBuilder)
{
this.objectDifferBuilder = objectDifferBuilder;
}
public boolean isIntrospectable(final DiffNode node)
{
final Class<?> nodeType = node.getValueType();
if (nodeType == null)
{
return false;
}
else if (isPrimitiveTypeEnumOrArray(nodeType))
{
return false;
}
else if (nodePathIntrospectionModeHolder.valueForNodePath(node.getPath()) == IntrospectionMode.DISABLED)
{
return false;
}
else if (typeIntrospectionModeMap.get(nodeType) == IntrospectionMode.DISABLED)
{
return false;
}
return true;
}
private static boolean isPrimitiveTypeEnumOrArray(final Class<?> nodeType)
{
return Classes.isPrimitiveType(nodeType)
|| Classes.isPrimitiveWrapperType(nodeType)
|| nodeType.isEnum()
|| nodeType.isArray();
}
public PropertyAccessExceptionHandler resolvePropertyAccessExceptionHandler(final Class<?> parentType, final String propertyName)
{
return defaultPropertyAccessExceptionHandler;
}
public TypeInfo typeInfoForNode(final DiffNode node)
{
final Class<?> beanType = node.getValueType();
final Introspector introspector = introspectorForNode(node);
final TypeInfo typeInfo = introspector.introspect(beanType);
typeInfo.setInstanceFactory(instanceFactory);
return typeInfo;
}
public Introspector introspectorForNode(final DiffNode node)
{
final Introspector typeIntrospector = typeIntrospectorMap.get(node.getValueType());
if (typeIntrospector != null)
{
return typeIntrospector;
}
final Introspector nodePathIntrospector = nodePathIntrospectorHolder.valueForNodePath(node.getPath());
if (nodePathIntrospector != null)
{
return nodePathIntrospector;
}
return defaultIntrospector;
}
public IntrospectionConfigurer setInstanceFactory(final InstanceFactory instanceFactory)
{
Assert.notNull(instanceFactory, "instanceFactory");
this.instanceFactory = new InstanceFactoryFallbackDecorator(instanceFactory);
return this;
}
public IntrospectionConfigurer setDefaultIntrospector(final Introspector introspector)
{
Assert.notNull(introspector, "The default introspector must not be null");
defaultIntrospector = introspector;
return this;
}
public IntrospectionConfigurer handlePropertyAccessExceptionsUsing(final PropertyAccessExceptionHandler exceptionHandler)
{
Assert.notNull(exceptionHandler, "exceptionHandler");
defaultPropertyAccessExceptionHandler = exceptionHandler;
return this;
}
public Of ofType(final Class<?> type)
{
return new Of()
{
public IntrospectionConfigurer toUse(final Introspector introspector)
{
typeIntrospectorMap.put(type, introspector);
return IntrospectionService.this;
}
public IntrospectionConfigurer toBeEnabled()
{
typeIntrospectionModeMap.put(type, IntrospectionMode.ENABLED);
return IntrospectionService.this;
}
public IntrospectionConfigurer toBeDisabled()
{
typeIntrospectionModeMap.put(type, IntrospectionMode.DISABLED);
return IntrospectionService.this;
}
};
}
public Of ofNode(final NodePath path)
{
return new Of()
{
public IntrospectionConfigurer toUse(final Introspector introspector)
{
nodePathIntrospectorHolder.put(path, introspector);
return IntrospectionService.this;
}
public IntrospectionConfigurer toBeEnabled()
{
nodePathIntrospectionModeHolder.put(path, IntrospectionMode.ENABLED);
return IntrospectionService.this;
}
public IntrospectionConfigurer toBeDisabled()
{
nodePathIntrospectionModeHolder.put(path, IntrospectionMode.DISABLED);
return IntrospectionService.this;
}
};
}
public ObjectDifferBuilder and()
{
return objectDifferBuilder;
}
public enum IntrospectionMode
{
ENABLED,
DISABLED
}
}