/*
* Copyright (c) 2010, Michael Grossmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.impl.base.blueprint.proxy.internal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
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 org.jowidgets.api.widgets.blueprint.convenience.ISetupBuilderConvenience;
import org.jowidgets.api.widgets.blueprint.convenience.ISetupBuilderConvenienceRegistry;
import org.jowidgets.api.widgets.blueprint.convenience.anotations.ConvenienceMethods;
import org.jowidgets.api.widgets.blueprint.defaults.IDefaultInitializer;
import org.jowidgets.api.widgets.blueprint.defaults.IDefaultsInitializerRegistry;
import org.jowidgets.api.widgets.blueprint.defaults.anotations.DefaultsInitializer;
import org.jowidgets.common.widgets.builder.ISetupBuilder;
import org.jowidgets.common.widgets.descriptor.IWidgetDescriptor;
import org.jowidgets.common.widgets.descriptor.setup.mandatory.Mandatory;
import org.jowidgets.common.widgets.descriptor.setup.mandatory.MandatoryCheckResult;
/**
* Achtung EXPERIMENTELL, soll so nicht bleiben ;-)
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class BluePrintProxyInvocationHandler implements InvocationHandler {
private final Map<String, Object> fields;
private final Map<MethodKey, ISetupBuilderConvenience<ISetupBuilder<?>>> convenienceMethodsMap;
private Class<? extends IWidgetDescriptor> bluePrintType;
private Class<? extends IWidgetDescriptor> widgetDescrType;
private final List<String> mandatoryFields;
public BluePrintProxyInvocationHandler() {
this.fields = new HashMap<String, Object>();
this.mandatoryFields = new ArrayList<String>();
this.convenienceMethodsMap = new HashMap<MethodKey, ISetupBuilderConvenience<ISetupBuilder<?>>>();
}
public void initialize(
final ISetupBuilder<?> proxy,
final Class<? extends IWidgetDescriptor> bluePrintType,
final ISetupBuilderConvenienceRegistry convenienceRegistry,
final IDefaultsInitializerRegistry defaultsRegistry) {
this.widgetDescrType = (Class<? extends IWidgetDescriptor>) getDescriptorInterface(bluePrintType);
if (this.widgetDescrType == null) {
throw new IllegalArgumentException("For the blueprint-type '"
+ bluePrintType
+ "' there are either multiple desriptors defined or there is no descriptor defined!");
}
this.bluePrintType = bluePrintType;
final List<Class<?>> visitedInterfaces = new ArrayList<Class<?>>();
initialize(
proxy,
(Class<? extends ISetupBuilder<ISetupBuilder<?>>>) bluePrintType,
convenienceRegistry,
defaultsRegistry,
visitedInterfaces);
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
try {
return doInvoke(proxy, method, args);
}
catch (final Exception e) {
throw new RuntimeException("Error while invoking method '" + method.getName() + "' on '" + proxy + "'", e);
}
}
public Object doInvoke(final Object proxy, final Method method, final Object[] args) throws Exception {
final MethodKey methodKey = new MethodKey(method);
if (method.getName().equals("toString")) {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("BluePrintType: " + bluePrintType.getName() + "\n");
stringBuilder.append("WidgetType: " + widgetDescrType.getName() + "\n");
stringBuilder.append("Fields: \n");
for (final Entry<String, Object> entry : fields.entrySet()) {
stringBuilder.append("Property: " + entry.getKey() + " \nValue: " + entry.getValue() + "\n");
}
return stringBuilder.toString();
}
else if (method.getName().equals("getDescriptorInterface")) {
return widgetDescrType;
}
else if (method.getName().equals("setSetup")) {
findMandatoryMethods();
final Object argument = args[0];
if (argument != null) {
for (final Method method2 : argument.getClass().getMethods()) {
if (method2.getParameterTypes().length == 0) {
if (method2.getName().contains("hashCode")) {
continue;
}
else if (method2.getName().contains("isProxyClass")) {
continue;
}
else if (method2.getName().startsWith("get")) {
final String fieldname = method2.getName().substring(3);
final Object value = method2.invoke(argument, (Object[]) null);
fields.put(fieldname, value);
}
else if (method2.getName().startsWith("has")) {
final String fieldname = method2.getName().substring(3);
final Object value = method2.invoke(argument, (Object[]) null);
fields.put(fieldname, value);
}
else if (method2.getName().startsWith("is")) {
final Object value = method2.invoke(argument, (Object[]) null);
final String fieldname = method2.getName().substring(2);
fields.put(fieldname, value);
}
}
}
return proxy;
}
else {
throw new IllegalArgumentException("argument of setDescriptor must not be null");
}
}
else if (convenienceMethodsMap.get(methodKey) != null) {
final ISetupBuilderConvenience<ISetupBuilder<?>> impl = convenienceMethodsMap.get(methodKey);
impl.setBuilder((ISetupBuilder<?>) proxy);
method.invoke(impl, args);
impl.setBuilder(null);
return proxy;
}
else if (method.getName().startsWith("get")) {
return fields.get(method.getName().substring(3));
}
else if (method.getName().startsWith("has")) {
return fields.get(method.getName().substring(3));
}
else if (method.getName().startsWith("is")) {
return fields.get(method.getName().substring(2));
}
else if (method.getName().startsWith("set")) {
fields.put(method.getName().substring(3), args[0]);
return proxy;
}
else if (method.getName().equals("checkMandatoryFields")) {
return checkMandatory();
}
else {
throw new IllegalStateException("'" + method.getName() + "' not known.");
}
}
private MandatoryCheckResult checkMandatory() {
MandatoryCheckResult result = MandatoryCheckResult.OK;
String unfilledFields = "";
for (final String mandatoryField : mandatoryFields) {
if (fields.get(mandatoryField) == null) {
unfilledFields += " " + mandatoryField;
}
}
if (!"".equals(unfilledFields)) {
result = new MandatoryCheckResult("The following fields are mandatory and not filled: " + unfilledFields);
}
return result;
}
private void findMandatoryMethods() {
final Method[] methods = ((Class<? extends ISetupBuilder<ISetupBuilder<?>>>) bluePrintType).getMethods();
if (methods != null) {
for (int i = 0; i < methods.length; i++) {
final Method method2 = methods[i];
if (null != method2.getAnnotation(Mandatory.class)) {
String name = method2.getName();
if (name.startsWith("get") || name.startsWith("has")) {
name = name.substring(3);
}
else if (name.startsWith("is")) {
name = name.substring(2);
}
else {
continue;
}
mandatoryFields.add(name);
}
}
}
}
private void initialize(
final ISetupBuilder<?> proxy,
final Class<? extends ISetupBuilder<ISetupBuilder<?>>> bluePrintType,
final ISetupBuilderConvenienceRegistry convenienceRegistry,
final IDefaultsInitializerRegistry defaultsRegistry,
final List<Class<?>> visitedInterfaces) {
for (final Class<?> superInterface : bluePrintType.getInterfaces()) {
if (!visitedInterfaces.contains(superInterface)) {
visitedInterfaces.add(superInterface);
initialize(
proxy,
(Class<? extends ISetupBuilder<ISetupBuilder<?>>>) superInterface,
convenienceRegistry,
defaultsRegistry,
visitedInterfaces);
}
}
for (final IDefaultInitializer<ISetupBuilder<?>> registeredInitializer : defaultsRegistry.getRegistered(bluePrintType)) {
registeredInitializer.initialize(proxy);
}
final IDefaultInitializer<ISetupBuilder<?>> defaultInitializer = getDefaultInitializer(bluePrintType);
if (defaultInitializer != null) {
defaultInitializer.initialize(proxy);
}
final ISetupBuilderConvenience<ISetupBuilder<?>> convenienceMethods = getConvenienceMethods(bluePrintType);
if (convenienceMethods != null) {
for (final Method method : convenienceMethods.getClass().getDeclaredMethods()) {
final MethodKey methodKey = new MethodKey(method);
convenienceMethodsMap.put(methodKey, convenienceMethods);
}
}
final ISetupBuilderConvenience<ISetupBuilder<?>> setupBuilderConvenience = convenienceRegistry.getRegistered(bluePrintType);
if (setupBuilderConvenience != null) {
for (final Method method : convenienceRegistry.getRegistered(bluePrintType).getClass().getDeclaredMethods()) {
final MethodKey methodKey = new MethodKey(method);
convenienceMethodsMap.put(methodKey, setupBuilderConvenience);
}
}
}
private ISetupBuilderConvenience<ISetupBuilder<?>> getConvenienceMethods(final Class<?> clazz) {
final ConvenienceMethods convenienceMethods = clazz.getAnnotation(ConvenienceMethods.class);
if (convenienceMethods != null) {
final Class<? extends ISetupBuilderConvenience<?>> convenienceMethodsClass = convenienceMethods.value();
if (convenienceMethodsClass != null) {
try {
return (ISetupBuilderConvenience<ISetupBuilder<?>>) convenienceMethodsClass.newInstance();
}
catch (final Exception e) {
throw new RuntimeException("Exception creating instance of '" + convenienceMethodsClass.getName() + "'");
}
}
}
return null;
}
private IDefaultInitializer<ISetupBuilder<?>> getDefaultInitializer(final Class<?> clazz) {
final DefaultsInitializer defaultsInitializer = clazz.getAnnotation(DefaultsInitializer.class);
if (defaultsInitializer != null) {
final Class<? extends IDefaultInitializer<?>> initializerClass = defaultsInitializer.value();
if (initializerClass != null) {
try {
return (IDefaultInitializer<ISetupBuilder<?>>) initializerClass.newInstance();
}
catch (final Exception e) {
throw new RuntimeException("Exception creating instance of '" + initializerClass.getName() + "'");
}
}
}
return null;
}
/**
* Get interfaces that the type inherits from.
*
* @param bluePrintType
* @return
*/
private static Class<?> getDescriptorInterface(final Class<?> bluePrintType) {
final Set<Class<?>> possibleInterfaces = getDescriptorInterfaces(bluePrintType);
if (possibleInterfaces.size() == 1) {
return possibleInterfaces.iterator().next();
}
return null;
}
private static Set<Class<?>> getDescriptorInterfaces(final Class<?> in) {
final Set<Class<?>> possibleInterfaces = new HashSet<Class<?>>();
if (isDescriptor(in)) {
possibleInterfaces.add(in);
}
final Class<?>[] interfaces = in.getInterfaces();
for (final Class<?> c : interfaces) {
possibleInterfaces.addAll(getDescriptorInterfaces(c));
}
return possibleInterfaces;
}
private static boolean isDescriptor(final Class<?> in) {
final Class<?>[] interfaces = in.getInterfaces();
for (final Class<?> c : interfaces) {
if (c == IWidgetDescriptor.class) {
return true;
}
}
return false;
}
}