package org.bimserver.shared.reflector;
/******************************************************************************
* Copyright (C) 2009-2014 BIMserver.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
import java.io.File;
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.NotFoundException;
import org.apache.commons.io.FileUtils;
import org.bimserver.shared.interfaces.PublicInterface;
import org.bimserver.shared.meta.SMethod;
import org.bimserver.shared.meta.SParameter;
import org.bimserver.shared.meta.SService;
import org.bimserver.shared.meta.SServicesMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RealtimeReflectorFactoryBuilder implements ReflectorFactoryBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(RealtimeReflectorFactoryBuilder.class);
private SServicesMap servicesMap;
private ClassPool pool;
private static int implementationCounter = 0;
private static final String GENERATED_CLASSES_PACKAGE = "org.bimserver.generated";
private File generatedClassesDir;
public RealtimeReflectorFactoryBuilder(SServicesMap servicesMap) {
this.servicesMap = servicesMap;
}
public RealtimeReflectorFactoryBuilder(SServicesMap servicesMap, File generatedClassesDir) {
this.servicesMap = servicesMap;
this.generatedClassesDir = generatedClassesDir;
}
public ReflectorFactory newReflectorFactory() {
implementationCounter++;
try {
pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(this.getClass()));
for (String name : servicesMap.keySetName()) {
SService sService = servicesMap.getByName(name);
build1((Class<? extends PublicInterface>) sService.getInterfaceClass(), sService);
build2((Class<? extends PublicInterface>) sService.getInterfaceClass(), sService);
}
CtClass reflectorFactoryImpl = pool.makeClass("org.bimserver.reflector.ReflectorFactoryImpl" + implementationCounter);
reflectorFactoryImpl.addInterface(pool.get(ReflectorFactory.class.getName()));
createCreateReflectorMethod1(reflectorFactoryImpl);
createCreateReflectorMethod2(reflectorFactoryImpl);
writeClassFile(reflectorFactoryImpl);
Class<?> class1 = pool.toClass(reflectorFactoryImpl, getClass().getClassLoader(), getClass().getProtectionDomain());
return (ReflectorFactory) class1.newInstance();
} catch (Exception e) {
LOGGER.error("", e);
}
return null;
}
private void writeClassFile(CtClass ctClass) throws IOException, CannotCompileException {
if (generatedClassesDir != null) {
File dir = new File(generatedClassesDir, ctClass.getPackageName().replace(".", "/"));
FileUtils.forceMkdir(dir);
FileUtils.writeByteArrayToFile(new File(dir, ctClass.getSimpleName() + ".class"), ctClass.toBytecode());
}
}
private void createCreateReflectorMethod2(CtClass reflectorFactoryImpl) throws NotFoundException, CannotCompileException {
CtClass[] parameters = new CtClass[2];
parameters[0] = pool.get(Class.class.getName());
parameters[1] = pool.get(PublicInterface.class.getName());
CtMethod method = new CtMethod(pool.get(Reflector.class.getName()), "createReflector", parameters, reflectorFactoryImpl);
StringBuilder methodBuilder = new StringBuilder();
methodBuilder.append("{");
methodBuilder.append("if (1==0) {");
for (String name : servicesMap.keySetName()) {
SService sService = servicesMap.getByName(name);
methodBuilder.append("} else if ($1.getSimpleName().equals(\"" + sService.getSimpleName() + "\")) {");
methodBuilder.append("return new " + GENERATED_CLASSES_PACKAGE + "." + sService.getSimpleName() + "Reflector" + implementationCounter + "((" + sService.getInterfaceClass().getName() + ")$2);");
}
methodBuilder.append("}");
methodBuilder.append("return null;");
methodBuilder.append("}");
method.setBody(methodBuilder.toString());
reflectorFactoryImpl.addMethod(method);
}
private void createCreateReflectorMethod1(CtClass reflectorFactoryImpl) throws NotFoundException, CannotCompileException {
CtClass[] parameters = new CtClass[2];
parameters[0] = pool.get(Class.class.getName());
parameters[1] = pool.get(Reflector.class.getName());
CtMethod method = new CtMethod(pool.get(PublicInterface.class.getName()), "createReflector", parameters, reflectorFactoryImpl);
StringBuilder methodBuilder = new StringBuilder();
methodBuilder.append("{");
methodBuilder.append("if (1==0) {");
for (String name : servicesMap.keySetName()) {
SService sService = servicesMap.getByName(name);
methodBuilder.append("} else if ($1.getSimpleName().equals(\"" + sService.getSimpleName() + "\")) {");
methodBuilder.append("return new " + GENERATED_CLASSES_PACKAGE + "." + sService.getSimpleName() + "Impl" + implementationCounter + "($2);");
}
methodBuilder.append("}");
methodBuilder.append("return null;");
methodBuilder.append("}");
method.setBody(methodBuilder.toString());
reflectorFactoryImpl.addMethod(method);
}
private void build1(Class<? extends PublicInterface> interfaceClass, org.bimserver.shared.meta.SService sService) {
try {
CtClass reflectorImplClass = pool.makeClass(GENERATED_CLASSES_PACKAGE + "." + interfaceClass.getSimpleName() + "Impl" + implementationCounter);
reflectorImplClass.addInterface(pool.get(interfaceClass.getName()));
CtClass reflectorClass = pool.get(Reflector.class.getName());
CtField reflectorField = new CtField(reflectorClass, "reflector", reflectorImplClass);
reflectorImplClass.addField(reflectorField);
CtConstructor constructor = new CtConstructor(new CtClass[] {reflectorClass}, reflectorImplClass);
StringBuilder sb = new StringBuilder();
reflectorImplClass.addConstructor(constructor);
sb.append("{");
sb.append("this.reflector = $1;");
sb.append("}");
constructor.setBody(sb.toString());
for (SMethod sMethod : sService.getMethods()) {
CtClass[] parameters = new CtClass[sMethod.getParameters().size()];
int i=0;
for (org.bimserver.shared.meta.SParameter sParameter : sMethod.getParameters()) {
parameters[i] = pool.get(sParameter.getType().toJavaCode());
i++;
}
CtMethod method = new CtMethod(pool.get(sMethod.getReturnType().toJavaCode()), sMethod.getName(), parameters, reflectorImplClass);
StringBuilder methodBuilder = new StringBuilder();
methodBuilder.append("{");
if (sMethod.getReturnType().isVoid()) {
} else {
methodBuilder.append("return (" + sMethod.getReturnType().toJavaCode() + ")");
}
methodBuilder.append("reflector.callMethod(\"" + interfaceClass.getSimpleName() + "\", \"" + sMethod.getName() + "\", " + sMethod.getReturnType().toJavaCode() + ".class");
if (sMethod.getParameters().isEmpty()) {
methodBuilder.append(", new " + KeyValuePair.class.getName() + "[0]");
} else {
methodBuilder.append(", new " + KeyValuePair.class.getName() + "[]{");
int x=1;
for (SParameter sParameter : sMethod.getParameters()) {
methodBuilder.append("new " + KeyValuePair.class.getName() + "(\"" + sParameter.getName() + "\", $" + x + ")");
if (sMethod.getParameter(sMethod.getParameters().size() - 1) != sParameter) {
methodBuilder.append(", ");
}
x++;
}
methodBuilder.append("}");
}
methodBuilder.append(");");
methodBuilder.append("}");
method.setBody(methodBuilder.toString());
reflectorImplClass.addMethod(method);
}
writeClassFile(reflectorImplClass);
pool.toClass(reflectorImplClass, getClass().getClassLoader(), getClass().getProtectionDomain());
} catch (Exception e) {
LOGGER.error("", e);
}
}
private void build2(Class<? extends PublicInterface> interfaceClass, org.bimserver.shared.meta.SService sService) {
try {
CtClass reflectorImplClass = pool.makeClass(GENERATED_CLASSES_PACKAGE + "." + interfaceClass.getSimpleName() + "Reflector" + implementationCounter);
CtClass reflectorClass = pool.get(Reflector.class.getName());
CtClass interfaceCtClass = pool.get(interfaceClass.getName());
reflectorImplClass.addInterface(reflectorClass);
CtField reflectorField = new CtField(interfaceCtClass, "publicInterface", reflectorImplClass);
reflectorImplClass.addField(reflectorField);
CtConstructor constructor = new CtConstructor(new CtClass[] {interfaceCtClass}, reflectorImplClass);
StringBuilder sb = new StringBuilder();
reflectorImplClass.addConstructor(constructor);
sb.append("{");
sb.append("this.publicInterface = $1;");
sb.append("}");
constructor.setBody(sb.toString());
CtClass[] parameters = new CtClass[4];
parameters[0] = pool.get(String.class.getName());
parameters[1] = pool.get(String.class.getName());
parameters[2] = pool.get(Class.class.getName());
parameters[3] = pool.get(KeyValuePair.class.getName() + "[]");
CtMethod method = new CtMethod(pool.get(Object.class.getName()), "callMethod", parameters, reflectorImplClass);
StringBuilder methodBuilder = new StringBuilder();
methodBuilder.append("{");
methodBuilder.append("if (1==0) {} ");
for (SMethod sMethod : sService.getMethods()) {
methodBuilder.append(" else if ($2.equals(\"" + sMethod.getName() + "\")) {");
if (!sMethod.getReturnType().isVoid()) {
methodBuilder.append("return ");
}
methodBuilder.append("publicInterface." + sMethod.getName() + "(");
int i=0;
for (SParameter sParameter : sMethod.getParameters()) {
methodBuilder.append("(" + sParameter.getType().toJavaCode() + ")$4[" + i + "].getValue()");
if (i < sMethod.getParameters().size() - 1) {
methodBuilder.append(", ");
}
i++;
}
methodBuilder.append(");");
methodBuilder.append("}");
}
methodBuilder.append("return null;");
methodBuilder.append("}");
method.setBody(methodBuilder.toString());
reflectorImplClass.addMethod(method);
writeClassFile(reflectorImplClass);
pool.toClass(reflectorImplClass, getClass().getClassLoader(), getClass().getProtectionDomain());
} catch (Exception e) {
LOGGER.error("", e);
}
}
}