/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jboss.arquillian.warp.impl.client.transformation;
import java.lang.reflect.Field;
import java.util.UUID;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.Modifier;
import javassist.bytecode.EnclosingMethodAttribute;
import javassist.bytecode.InnerClassesAttribute;
import org.jboss.arquillian.warp.Inspection;
import org.jboss.shrinkwrap.api.asset.NamedAsset;
/**
* Allows to transform inner classes to be usable as top-level classes.
*
* @author Lukas Fryc
*/
public class TransformedInspection {
private ClassPool classPool;
private Class<?> originalClass;
private CtClass transformed;
private Class<Inspection> transformedClass;
private Inspection transformedInspection;
public TransformedInspection(Inspection inspection) throws InspectionTransformationException {
this(inspection.getClass(), "org.jboss.arquillian.warp.generated.A" + UUID.randomUUID().toString(), inspection);
}
private TransformedInspection(Class<?> originalClass, String newClassName, Inspection serverInspection)
throws InspectionTransformationException {
this.classPool = ClassPool.getDefault();
this.originalClass = originalClass;
this.transformed = transform(newClassName);
this.transformedClass = toClass();
this.transformedInspection = cloneToNew(serverInspection);
}
private CtClass transform(String newClassName) throws InspectionTransformationException {
try {
CtClass output = classPool.getAndRename(originalClass.getName(), newClassName);
// remove enclosing reference to the method.
output.getClassFile2()
.getAttributes()
.remove(output.getClassFile2().getAttribute(EnclosingMethodAttribute.tag));
output.getClassFile2().getAttributes().remove(output.getClassFile2().getAttribute(InnerClassesAttribute.tag));
output.setModifiers(Modifier.PUBLIC);
for (CtField field : output.getDeclaredFields()) {
if (field.getName().equals("this$0")) {
output.removeField(field);
}
}
CtField field = output.getField("serialVersionUID");
if (field.getDeclaringClass() != output) {
throw new NoSerialVersionUIDException("serialVersionUID for class " + originalClass.getName()
+ " is not set; please set serialVersionUID to allow Warp work correctly");
}
for (CtConstructor constructor : output.getConstructors()) {
output.removeConstructor(constructor);
}
output.addConstructor(CtNewConstructor.defaultConstructor(output));
return output;
} catch (Exception e) {
throw new InspectionTransformationException(
"Unable to transform inspection " + originalClass.getName() + ":\n" + e.getMessage(), e);
}
}
private Inspection cloneToNew(Inspection obj) throws InspectionTransformationException {
try {
Class<? extends Inspection> oldClass = obj.getClass();
Inspection newObj = transformedClass.newInstance();
for (Field newF : transformedClass.getDeclaredFields()) {
if (java.lang.reflect.Modifier.isStatic(newF.getModifiers())
&& java.lang.reflect.Modifier.isFinal(newF.getModifiers())) {
continue;
}
Field oldF = oldClass.getDeclaredField(newF.getName());
oldF.setAccessible(true);
newF.setAccessible(true);
newF.set(newObj, oldF.get(obj));
}
return newObj;
} catch (Exception e) {
throw new InspectionTransformationException("Unable to clone " + obj.getClass().getName() + " to "
+ transformed.getName(), e);
}
}
public byte[] toBytecode() throws InspectionTransformationException {
try {
return transformed.toBytecode();
} catch (Exception e) {
throw new InspectionTransformationException("Unable to convert " + transformed.getName() + " to bytecode", e);
}
}
public NamedAsset toShrinkWrapAsset() {
return new CtClassAsset(transformed);
}
public Class<?> getTransformedClass() {
return transformedClass;
}
public Inspection getTransformedInspection() {
return transformedInspection;
}
@SuppressWarnings("unchecked")
private Class<Inspection> toClass() throws InspectionTransformationException {
try {
return transformed.toClass();
} catch (Exception e) {
throw new InspectionTransformationException("Unable to convert " + transformed.getName() + " to class", e);
}
}
public Class<?> getOriginalClass() {
return originalClass;
}
}