blob: 3bcdc8f871e070520f96750be2383c19697d7e3c [file] [log] [blame]
// Copyright 2009, 2010 The Apache Software Foundation
//
// 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 org.apache.tapestry5.ioc.internal.services;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Map;
import org.apache.tapestry5.ioc.ObjectCreator;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.ioc.services.Builtin;
import org.apache.tapestry5.ioc.services.ClassFab;
import org.apache.tapestry5.ioc.services.ClassFabUtils;
import org.apache.tapestry5.ioc.services.ClassFactory;
import org.apache.tapestry5.ioc.services.MethodIterator;
import org.apache.tapestry5.ioc.services.MethodSignature;
import org.apache.tapestry5.ioc.services.ThunkCreator;
@SuppressWarnings("all")
public class ThunkCreatorImpl implements ThunkCreator
{
/**
* Map from an interface type to a corresponding "thunk" class that implements the interface.
*/
private final Map<Class, Class> interfaceToThunkClass = CollectionFactory.newConcurrentMap();
private final ClassFactory classFactory;
private final MethodSignature toStringSignature = new MethodSignature(String.class, "toString", null, null);
private static final int PRIVATE_FINAL = Modifier.FINAL + Modifier.PRIVATE;
private static final String DESCRIPTION_FIELD = "_$description";
private static final String CREATOR_FIELD = "_$creator";
private static final String DELEGATE_METHOD = "_$delegate";
public ThunkCreatorImpl(@Builtin ClassFactory classFactory)
{
this.classFactory = classFactory;
}
public <T> T createThunk(Class<T> proxyType, ObjectCreator objectCreator, String description)
{
assert proxyType != null;
assert objectCreator != null;
assert InternalUtils.isNonBlank(description);
if (!proxyType.isInterface())
throw new IllegalArgumentException(
String.format("Thunks may only be created for interfaces; %s is a class.",
ClassFabUtils.toJavaClassName(proxyType)));
final Class thunkClass = getThunkClass(proxyType);
Throwable failure;
try
{
return proxyType.cast(thunkClass.getConstructors()[0].newInstance(description, objectCreator));
}
catch (InvocationTargetException ex)
{
failure = ex.getTargetException();
}
catch (Exception ex)
{
failure = ex;
}
throw new RuntimeException(String.format("Exception instantiating thunk class %s: %s",
thunkClass.getName(),
InternalUtils.toMessage(failure)),
failure);
}
private Class getThunkClass(Class type)
{
Class result = interfaceToThunkClass.get(type);
if (result == null)
{
result = constructThunkClass(type);
interfaceToThunkClass.put(type, result);
}
return result;
}
private Class constructThunkClass(Class interfaceType)
{
ClassFab classFab = classFactory.newClass(interfaceType);
classFab.addField(DESCRIPTION_FIELD, PRIVATE_FINAL, String.class);
classFab.addField(CREATOR_FIELD, PRIVATE_FINAL, ObjectCreator.class);
classFab.addConstructor(new Class[] { String.class, ObjectCreator.class }, null,
String.format("{ %s = $1; %s = $2; }", DESCRIPTION_FIELD, CREATOR_FIELD));
MethodSignature sig = new MethodSignature(interfaceType, DELEGATE_METHOD, null, null);
classFab.addMethod(Modifier.PRIVATE, sig, String.format("return ($r) %s.createObject();", CREATOR_FIELD));
MethodIterator mi = new MethodIterator(interfaceType);
while (mi.hasNext())
{
sig = mi.next();
classFab.addMethod(Modifier.PUBLIC, sig,
String.format("return ($r) %s().%s($$);", DELEGATE_METHOD, sig.getName()));
}
if (!mi.getToString())
classFab.addMethod(Modifier.PUBLIC, toStringSignature, String.format("return %s;", DESCRIPTION_FIELD));
return classFab.createClass();
}
}