package org.kevoree.annotator;
import javassist.*;
import jet.runtime.typeinfo.JetValueParameter;
import org.jetbrains.annotations.NotNull;
import org.kevoree.*;
import org.kevoree.Package;
import org.kevoree.annotation.Input;
import org.kevoree.annotation.Output;
import org.kevoree.annotation.Param;
import org.kevoree.api.*;
import org.kevoree.factory.KevoreeFactory;
import org.kevoree.pmodeling.api.KMFContainer;
import org.kevoree.pmodeling.api.util.ModelVisitor;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
/**
* Created by duke on 23/01/2014.
* <p/>
*/
public class ModelBuilderHelper {
public static void deepMethods(CtClass clazz, KevoreeFactory factory, TypeDefinition currentTypeDefinition) throws Exception {
for (CtMethod method : clazz.getDeclaredMethods()) {
for (Object annotation : method.getAnnotations()) {
if (annotation instanceof Input) {
Input annotationInput = (Input) annotation;
if (currentTypeDefinition instanceof ComponentType) {
ComponentType currentTypeDefinitionCT = (ComponentType) currentTypeDefinition;
PortTypeRef providedPortRef = factory.createPortTypeRef();
providedPortRef.setName(method.getName());
try {
providedPortRef.setOptional(annotationInput.optional());
} catch (Exception e) {
providedPortRef.setOptional(true);
}
currentTypeDefinitionCT.addProvided(providedPortRef);
}
}
if (annotation instanceof Param && (method.getName().startsWith("set") || method.getName().startsWith("get"))) {
boolean checkType = false;
String cleanedName = method.getName().substring(3);
cleanedName = cleanedName.substring(0, 1).toLowerCase() + cleanedName.substring(1);
Param annotationParam = (Param) annotation;
DataType dataType = null;
if (method.getReturnType().getName().equals(String.class.getName())) {
checkType = true;
dataType = DataType.STRING;
}
if (method.getReturnType().getName().equals(Float.class.getName()) || method.getReturnType().getName().equals("float")) {
checkType = true;
dataType = DataType.FLOAT;
}
if (method.getReturnType().getName().equals(Integer.class.getName()) || method.getReturnType().getName().equals("int")) {
checkType = true;
dataType = DataType.INT;
}
if (method.getReturnType().getName().equals(Double.class.getName()) || method.getReturnType().getName().equals("double")) {
checkType = true;
dataType = DataType.DOUBLE;
}
if (method.getReturnType().getName().equals(Boolean.class.getName()) || method.getReturnType().getName().equals("boolean")) {
checkType = true;
dataType = DataType.BOOLEAN;
}
if (method.getReturnType().getName().equals(Long.class.getName()) || method.getReturnType().getName().equals("long")) {
checkType = true;
dataType = DataType.LONG;
}
if (method.getReturnType().getName().equals(Byte.class.getName()) || method.getReturnType().getName().equals(byte.class.getName()) || method.getReturnType().getName().equals("byte")) {
checkType = true;
dataType = DataType.BYTE;
}
if (method.getReturnType().getName().equals(char.class.getName()) || method.getReturnType().getName().equals("char")) {
checkType = true;
dataType = DataType.CHAR;
}
if (method.getReturnType().getName().equals(Short.class.getName()) || method.getReturnType().getName().equals("short")) {
checkType = true;
dataType = DataType.SHORT;
}
if (!checkType) {
if (!method.getReturnType().isPrimitive()) {
throw new Exception("Param annotation is only applicable on field of type String,Long,Double,Float,Integer, current " + method.getReturnType().getName());
}
}
DictionaryAttribute dicAtt = factory.createDictionaryAttribute();
if (currentTypeDefinition.getDictionaryType() == null) {
currentTypeDefinition.setDictionaryType(factory.createDictionaryType());
}
dicAtt.setName(cleanedName);
dicAtt.setDatatype(dataType);
try {
dicAtt.setOptional(annotationParam.optional());
} catch (Exception e) {
dicAtt.setOptional(true);
}
try {
dicAtt.setFragmentDependant(annotationParam.fragmentDependent());
} catch (Exception e) {
dicAtt.setFragmentDependant(false);
}
try {
dicAtt.setDefaultValue(annotationParam.defaultValue());
} catch (Exception e) {
dicAtt.setDefaultValue("");
}
currentTypeDefinition.getDictionaryType().addAttributes(dicAtt);
}
}
}
for (CtClass interfaceLoop : clazz.getInterfaces()) {
deepMethods(interfaceLoop, factory, currentTypeDefinition);
}
if (clazz.getSuperclass() != null) {
deepMethods(clazz.getSuperclass(), factory, currentTypeDefinition);
}
}
public static void deepFields(CtClass clazz, KevoreeFactory factory, TypeDefinition currentTypeDefinition) throws Exception {
for (CtField field : clazz.getDeclaredFields()) {
for (Object annotation : field.getAnnotations()) {
if (annotation instanceof org.kevoree.annotation.KevoreeInject) {
boolean checkType = false;
if (field.getType().getName().equals(ModelService.class.getName())) {
checkType = true;
} else if (field.getType().getName().equals(BootstrapService.class.getName())) {
checkType = true;
} else if (field.getType().getName().equals(KevScriptService.class.getName())) {
checkType = true;
} else if (field.getType().getName().equals(Context.class.getName())) {
checkType = true;
} else if (field.getType().getName().equals(ChannelContext.class.getName())) {
checkType = true;
} else if (field.getType().getName().equals(PlatformService.class.getName())) {
checkType = true;
}
if (!checkType) {
throw new Exception("KevoreeInject annotation is only suitable for following types : ModelService,BootstrapService,KevScriptService,Context,ChannelContext : currently found : " + field.getType().getName());
}
}
if (annotation instanceof Output) {
Output annotationOutput = (Output) annotation;
if (!field.getType().getName().equals(org.kevoree.api.Port.class.getName())) {
throw new Exception("Output port field must of type of " + org.kevoree.api.Port.class.getName());
}
if (currentTypeDefinition instanceof ComponentType) {
ComponentType currentTypeDefinitionComponentType = (ComponentType) currentTypeDefinition;
PortTypeRef requiredPortRef = factory.createPortTypeRef();
requiredPortRef.setName(field.getName());
try {
requiredPortRef.setOptional(annotationOutput.optional());
} catch (Exception e) {
requiredPortRef.setOptional(true);
}
currentTypeDefinitionComponentType.addRequired(requiredPortRef);
}
}
if (annotation instanceof Param) {
Param annotationParam = (Param) annotation;
DataType dataType = null;
boolean checkType = false;
if (field.getType().getName().equals(String.class.getName())) {
checkType = true;
dataType = DataType.STRING;
}
if (field.getType().getName().equals(Float.class.getName()) || field.getType().getName().equals("float")) {
checkType = true;
dataType = DataType.FLOAT;
}
if (field.getType().getName().equals(Integer.class.getName()) || field.getType().getName().equals("int")) {
checkType = true;
dataType = DataType.INT;
}
if (field.getType().getName().equals(Double.class.getName()) || field.getType().getName().equals("double")) {
checkType = true;
dataType = DataType.DOUBLE;
}
if (field.getType().getName().equals(Boolean.class.getName()) || field.getType().getName().equals("boolean")) {
checkType = true;
dataType = DataType.BOOLEAN;
}
if (field.getType().getName().equals(Long.class.getName()) || field.getType().getName().equals("long")) {
checkType = true;
dataType = DataType.LONG;
}
if (field.getType().getName().equals(Short.class.getName()) || field.getType().getName().equals("short")) {
checkType = true;
dataType = DataType.SHORT;
}
if (field.getType().getName().equals(char.class.getName()) || field.getType().getName().equals("char")) {
checkType = true;
dataType = DataType.CHAR;
}
if (field.getType().getName().equals(byte.class.getName()) || field.getType().getName().equals("byte") || field.getType().getName().equals(Byte.class.getName())) {
checkType = true;
dataType = DataType.BYTE;
}
if (!checkType) {
if (!field.getType().isPrimitive()) {
throw new Exception("Param annotation is only applicable on field of type String,Long,Double,Float,Integer, current " + field.getType().getName());
}
}
DictionaryAttribute dicAtt = factory.createDictionaryAttribute();
if (currentTypeDefinition.getDictionaryType() == null) {
currentTypeDefinition.setDictionaryType(factory.createDictionaryType());
}
dicAtt.setName(field.getName());
dicAtt.setDatatype(dataType);
try {
dicAtt.setOptional(annotationParam.optional());
} catch (Exception e) {
dicAtt.setOptional(true);
}
try {
dicAtt.setFragmentDependant(annotationParam.fragmentDependent());
} catch (Exception e) {
dicAtt.setFragmentDependant(false);
}
try {
dicAtt.setDefaultValue(annotationParam.defaultValue());
} catch (Exception e) {
dicAtt.setDefaultValue(null);
}
currentTypeDefinition.getDictionaryType().addAttributes(dicAtt);
}
}
}
for (CtClass interfaceLoop : clazz.getInterfaces()) {
deepFields(interfaceLoop, factory, currentTypeDefinition);
}
if (clazz.getSuperclass() != null) {
deepFields(clazz.getSuperclass(), factory, currentTypeDefinition);
}
}
private static void checkParent(TypeDefinition current, CtClass clazz, CtClass originClazz, ContainerRoot root, KevoreeFactory factory) throws Exception {
if (clazz == null) {
return;
}
String name = clazz.getName();
String version = null;
String currentTypeName = null;
try {
for (Object an : clazz.getAnnotations()) {
String newMeta = metaClassName(an);
if (newMeta != null) {
if (currentTypeName != null) {
throw new Exception("A Java Class can't be mapped to several Kevoree TypeDefinition " + clazz.getName());
} else {
currentTypeName = newMeta;
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (currentTypeName != null) {
String endPath = clazz.getName().replace(".", File.separator) + ".class";
String baseURL = clazz.getURL().toString().replace(endPath, "");
/*
String manifest = baseURL + "META-INF" + File.separator + "MANIFEST.MF";
URL u2 = new URL(manifest);
System.out.println(u2.openStream().available());
*/
/*
if (version == null) {
File metaInf = new File(baseURL + "META-INF" + File.separator + "maven");
System.out.println(metaInf.exists());
if (metaInf.exists()) {
if (metaInf.listFiles().length > 0) {
File groupFile = metaInf.listFiles()[0];
}
}
} */
if (version == null) {
try {
String kevManifest = baseURL + "KEV-INF" + File.separator + "lib.json";
URL ukevManifest = new URL(kevManifest);
InputStream is = ukevManifest.openStream();
ContainerRoot libModel = (ContainerRoot) factory.createJSONLoader().loadModelFromStream(is).get(0);
factory.root(libModel);
final HashMap<DeployUnit, Integer> links = new HashMap<DeployUnit, Integer>();
libModel.deepVisitContained(new ModelVisitor() {
@Override
public void visit(@JetValueParameter(name = "elem") @NotNull KMFContainer kmfContainer, @JetValueParameter(name = "refNameInParent") @NotNull String s, @JetValueParameter(name = "parent") @NotNull KMFContainer kmfContainer2) {
if (kmfContainer instanceof DeployUnit) {
DeployUnit du = (DeployUnit) kmfContainer;
if (!links.containsKey(du)) {
links.put(du, 0);
}
for (DeployUnit dul : du.getRequiredLibs()) {
if (!links.containsKey(dul)) {
links.put(dul, 0);
}
links.put(dul, links.get(dul) + 1);
}
}
}
});
//This current deploy unit should be the only one with no external references, tricky part
for (DeployUnit d : links.keySet()) {
if (links.get(d) == 0) {
version = d.getVersion();
}
}
is.close();
} catch (Exception e) {
//
}
}
if (version == null) {
version = current.getVersion();
}
TypeDefinition parent = getOrCreateTypeDefinition(name, version, root, factory, currentTypeName);
current.addSuperTypes(parent);
}
}
private static void processTypeDefinition(TypeDefinition td, DeployUnit du, CtClass clazz, ContainerRoot root, KevoreeFactory factory) throws Exception {
if (Modifier.isAbstract(clazz.getModifiers())) {
td.setAbstract(true);
} else {
td.setAbstract(false);
}
final Value javaClazz = factory.createValue();
javaClazz.setName(td.getName() + ":" + td.getVersion() + ":java.class");
javaClazz.setValue(clazz.getName());
du.addFilters(javaClazz);
td.addDeployUnits(du);
try {
checkParent(td, clazz.getSuperclass(), clazz, root, factory);
} catch (NotFoundException e) {
e.printStackTrace();
}
try {
for (CtClass interf : clazz.getInterfaces()) {
checkParent(td, interf, clazz, root, factory);
}
} catch (NotFoundException e) {
e.printStackTrace();
}
}
public static TypeDefinition getOrCreateTypeDefinition(String name, String version, ContainerRoot root, KevoreeFactory factory, String typeName) {
String[] packages = name.split("\\.");
if (packages.length <= 1) {
throw new RuntimeException("Component '" + name + "' must be defined in a Java package");
}
org.kevoree.Package pack = null;
for (int i = 0; i < packages.length - 1; i++) {
if (pack == null) {
pack = root.findPackagesByID(packages[i]);
if (pack == null) {
pack = (org.kevoree.Package) factory.createPackage().withName(packages[i]);
root.addPackages(pack);
}
} else {
Package packNew = pack.findPackagesByID(packages[i]);
if (packNew == null) {
packNew = (org.kevoree.Package) factory.createPackage().withName(packages[i]);
pack.addPackages(packNew);
}
pack = packNew;
}
}
String tdName = packages[packages.length - 1];
TypeDefinition foundTD = pack.findTypeDefinitionsByNameVersion(tdName, version);
if (foundTD != null) {
return foundTD;
} else {
TypeDefinition td = (TypeDefinition) factory.create(typeName);
td.setVersion(version);
td.setName(tdName);
pack.addTypeDefinitions(td);
return td;
}
}
public static void process(Object elem, CtClass clazz, KevoreeFactory factory, DeployUnit du, ContainerRoot root) throws Exception {
if (elem instanceof org.kevoree.annotation.GroupType) {
TypeDefinition td = getOrCreateTypeDefinition(clazz.getName(), du.getVersion(), root, factory, metaClassName(elem));
processTypeDefinition(td, du, clazz, root, factory);
deepFields(clazz, factory, td);
deepMethods(clazz, factory, td);
}
if (elem instanceof org.kevoree.annotation.ChannelType) {
TypeDefinition td = getOrCreateTypeDefinition(clazz.getName(), du.getVersion(), root, factory, metaClassName(elem));
processTypeDefinition(td, du, clazz, root, factory);
deepFields(clazz, factory, td);
deepMethods(clazz, factory, td);
}
if (elem instanceof org.kevoree.annotation.ComponentType) {
TypeDefinition td = getOrCreateTypeDefinition(clazz.getName(), du.getVersion(), root, factory, metaClassName(elem));
processTypeDefinition(td, du, clazz, root, factory);
deepFields(clazz, factory, td);
deepMethods(clazz, factory, td);
}
if (elem instanceof org.kevoree.annotation.NodeType) {
TypeDefinition td = getOrCreateTypeDefinition(clazz.getName(), du.getVersion(), root, factory, metaClassName(elem));
processTypeDefinition(td, du, clazz, root, factory);
deepFields(clazz, factory, td);
deepMethods(clazz, factory, td);
}
}
public static String metaClassName(Object elem) {
if (elem instanceof org.kevoree.annotation.GroupType) {
return org.kevoree.GroupType.class.getName();
}
if (elem instanceof org.kevoree.annotation.ChannelType) {
return org.kevoree.ChannelType.class.getName();
}
if (elem instanceof org.kevoree.annotation.ComponentType) {
return org.kevoree.ComponentType.class.getName();
}
if (elem instanceof org.kevoree.annotation.NodeType) {
return org.kevoree.NodeType.class.getName();
}
return null;
}
}