/*
* Copyright (c) 2010-2016 Evolveum
*
* 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 com.evolveum.midpoint.schema.xjc.schema;
import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismReference;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.Raw;
import com.evolveum.midpoint.prism.Referencable;
import com.evolveum.midpoint.prism.xjc.PrismContainerArrayList;
import com.evolveum.midpoint.prism.xjc.PrismForJAXBUtil;
import com.evolveum.midpoint.prism.xjc.PrismReferenceArrayList;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.schema.xjc.PrefixMapper;
import com.evolveum.midpoint.schema.xjc.Processor;
import com.evolveum.prism.xml.ns._public.types_3.ObjectReferenceType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import com.sun.codemodel.*;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.model.CClassInfo;
import com.sun.tools.xjc.model.CElementInfo;
import com.sun.tools.xjc.model.CPropertyInfo;
import com.sun.tools.xjc.model.CTypeInfo;
import com.sun.tools.xjc.model.nav.NClass;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.Outline;
import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIDeclaration;
import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIXPluginCustomization;
import com.sun.xml.xsom.XSComponent;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSType;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import org.jvnet.jaxb2_commons.lang.Equals;
import org.jvnet.jaxb2_commons.lang.HashCode;
import org.w3c.dom.Element;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import javax.xml.bind.annotation.*;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import static com.evolveum.midpoint.schema.xjc.util.ProcessorUtils.*;
/**
* Custom XJC plugin used to update JAXB classes implementation and use Prism stuff as
* internal data representation.
*
* @author lazyman
*/
public class SchemaProcessor implements Processor {
private static final boolean PRINT_DEBUG_INFO = false;
//qname for object reference type
private static final QName OBJECT_REFERENCE_TYPE = new QName(PrefixMapper.C.getNamespace(), "ObjectReferenceType");
public static final QName A_OBJECT_REFERENCE = new QName(PrefixMapper.A.getNamespace(), "objectReference");
//annotations for schema processor
public static final QName A_PRISM_CONTAINER = new QName(PrefixMapper.A.getNamespace(), "container");
public static final QName A_PRISM_OBJECT = new QName(PrefixMapper.A.getNamespace(), "object");
public static final QName A_RAW_TYPE = new QName(PrefixMapper.A.getNamespace(), "rawType");
//Public fields
private static final String COMPLEX_TYPE_FIELD_NAME = "COMPLEX_TYPE";
// Public generated methods
// The "as" prefix is chosen to avoid clash with usual "get" for the fields and also to indicate that
// the it returns the same object in a different representation and not a composed/aggregated object
public static final String METHOD_AS_PRISM_OBJECT = "asPrismObject";
public static final String METHOD_AS_PRISM_CONTAINER_VALUE = "asPrismContainerValue";
private static final String METHOD_AS_PRISM_CONTAINER = "asPrismContainer";
// The "setup" prefix is chosen avoid collision with regular setters for generated fields
public static final String METHOD_SETUP_CONTAINER_VALUE = "setupContainerValue";
public static final String METHOD_SETUP_CONTAINER = "setupContainer";
public static final String METHOD_AS_REFERENCE_VALUE = "asReferenceValue";
public static final String METHOD_SETUP_REFERENCE_VALUE = "setupReferenceValue";
// Internal fields and methods. Although some of these fields needs to be public (so they can be used by
// prism classes), they are not really intended for public usage. We also want to avoid conflicts with code
// generated for regular fields. Hence the underscore.
private static final String CONTAINER_FIELD_NAME = "_container";
private static final String CONTAINER_VALUE_FIELD_NAME = "_containerValue";
private static final String METHOD_GET_CONTAINER_NAME = "_getContainerName";
private static final String METHOD_GET_CONTAINER_TYPE = "_getContainerType";
private static final String REFERENCE_VALUE_FIELD_NAME = "_referenceValue";
//methods in PrismForJAXBUtil
private static final String METHOD_PRISM_UTIL_GET_FIELD_SINGLE_CONTAINERABLE = "getFieldSingleContainerable";
private static final String METHOD_PRISM_UTIL_GET_PROPERTY_VALUE = "getPropertyValue";
private static final String METHOD_PRISM_UTIL_GET_PROPERTY_VALUES = "getPropertyValues";
private static final String METHOD_PRISM_UTIL_SET_PROPERTY_VALUE = "setPropertyValue";
private static final String METHOD_PRISM_UTIL_GET_CONTAINER = "getContainer";
private static final String METHOD_PRISM_UTIL_SET_FIELD_CONTAINER_VALUE = "setFieldContainerValue";
private static final String METHOD_PRISM_UTIL_GET_REFERENCE = "getReference";
private static final String METHOD_PRISM_UTIL_GET_REFERENCE_VALUE = "getReferenceValue";
private static final String METHOD_PRISM_UTIL_SET_REFERENCE_VALUE_AS_REF = "setReferenceValueAsRef";
private static final String METHOD_PRISM_UTIL_SET_REFERENCE_VALUE_AS_OBJECT = "setReferenceValueAsObject";
private static final String METHOD_PRISM_UTIL_GET_REFERENCE_FILTER_CLAUSE_XNODE = "getReferenceFilterClauseXNode";
private static final String METHOD_PRISM_UTIL_SET_REFERENCE_FILTER_CLAUSE_XNODE = "setReferenceFilterClauseXNode";
private static final String METHOD_PRISM_UTIL_GET_REFERENCE_TARGET_NAME = "getReferenceTargetName";
private static final String METHOD_PRISM_UTIL_SET_REFERENCE_TARGET_NAME = "setReferenceTargetName";
private static final String METHOD_PRISM_UTIL_OBJECTABLE_AS_REFERENCE_VALUE = "objectableAsReferenceValue";
private static final String METHOD_PRISM_UTIL_SETUP_CONTAINER_VALUE = "setupContainerValue";
private static final String METHOD_PRISM_UTIL_CREATE_TARGET_INSTANCE = "createTargetInstance";
// ???
private static final String METHOD_PRISM_GET_ANY = "getAny";
private static final String METHOD_CONTAINER_GET_VALUE = "getValue";
private static final String CONTAINER_VALUE_LOCAL_VAR_NAME = "containerValue";
private static final String FIELD_CONTAINER_VALUE_LOCAL_VAR_NAME = "fieldContainerValue";
private static final String OBJECT_LOCAL_FIELD_NAME = "object";
private static final String REFERENCE_LOCAL_VARIABLE_NAME = "reference";
//equals, toString, hashCode methods
private static final String METHOD_TO_STRING = "toString";
private static final String METHOD_DEBUG_DUMP = "debugDump";
private static final int METHOD_DEBUG_DUMP_INDENT = 3;
private static final String METHOD_EQUALS = "equals";
private static final String METHOD_EQUIVALENT = "equivalent";
private static final String METHOD_HASH_CODE = "hashCode";
//referenced class map
private static final Map<Class, JClass> CLASS_MAP = new HashMap<Class, JClass>() {
@Override
public JClass get(Object o) {
JClass clazz = super.get(o);
Validate.notNull(clazz, "Class '" + o + "' not registered.");
return clazz;
}
};
@Override
public boolean run(Outline outline, Options options, ErrorHandler errorHandler) throws SAXException {
try {
createClassMap(CLASS_MAP, outline, PrismReferenceValue.class, PrismReference.class, PrismObject.class,
String.class, Object.class, XmlTransient.class, Override.class, IllegalArgumentException.class,
QName.class, PrismForJAXBUtil.class, PrismReferenceArrayList.class, PrismContainerValue.class,
List.class, Objectable.class, StringBuilder.class, XmlAccessorType.class, XmlElement.class, XmlType.class,
XmlAttribute.class, XmlAnyAttribute.class, XmlAnyElement.class, PrismContainer.class, Equals.class,
PrismContainerArrayList.class, HashCode.class, PrismContainerDefinition.class, Containerable.class,
Referencable.class, Raw.class, Enum.class, XmlEnum.class, PolyStringType.class, XmlTypeConverter.class);
StepSchemaConstants stepSchemaConstants = new StepSchemaConstants();
stepSchemaConstants.run(outline, options, errorHandler);
Map<String, JFieldVar> namespaceFields = stepSchemaConstants.getNamespaceFields();
addComplextType(outline, namespaceFields);
addContainerName(outline, namespaceFields);
addFieldQNames(outline, namespaceFields);
updatePrismObject(outline);
updatePrismContainer(outline);
updateFields(outline);
updateObjectReferenceType(outline);
updateObjectFactoryElements(outline);
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("Couldn't process MidPoint JAXB customisation, reason: "
+ ex.getMessage() + ", " + ex.getClass(), ex);
}
return true;
}
private void createClassMap(Map<Class, JClass> classMap, Outline outline, Class... classes) {
for (Class clazz : classes) {
classMap.put(clazz, (JClass) outline.getModel().codeModel._ref(clazz));
}
}
private void updateObjectReferenceType(Outline outline) {
ClassOutline objectReferenceOutline = null;
for (Map.Entry<NClass, CClassInfo> entry : outline.getModel().beans().entrySet()) {
QName qname = entry.getValue().getTypeName();
if (qname == null || !OBJECT_REFERENCE_TYPE.equals(qname)) {
continue;
}
objectReferenceOutline = outline.getClazz(entry.getValue());
break;
}
if (objectReferenceOutline == null) {
//object reference type class not found
return;
}
updateClassAnnotation(objectReferenceOutline);
JDefinedClass definedClass = objectReferenceOutline.implClass;
definedClass._implements(CLASS_MAP.get(Referencable.class));
createDefaultConstructor(definedClass);
//add prism reference and get/set method for it
JVar reference = definedClass.field(JMod.PRIVATE, PrismReferenceValue.class, REFERENCE_VALUE_FIELD_NAME);
JMethod getReference = definedClass.method(JMod.PUBLIC, PrismReferenceValue.class, METHOD_AS_REFERENCE_VALUE);
// getReference.annotate(CLASS_MAP.get(XmlTransient.class));
JBlock body = getReference.body();
JBlock then = body._if(reference.eq(JExpr._null()))._then();
JInvocation newReference = JExpr._new(CLASS_MAP.get(PrismReferenceValue.class));
then.assign(reference, newReference);
body._return(reference);
JMethod setReference = definedClass.method(JMod.PUBLIC, void.class, METHOD_SETUP_REFERENCE_VALUE);
JVar value = setReference.param(PrismReferenceValue.class, "value");
body = setReference.body();
body.assign(reference, value);
//update for oid methods
updateObjectReferenceOid(definedClass, getReference);
//update for type methods
updateObjectReferenceType(definedClass, getReference);
updateObjectReferenceRelation(definedClass, getReference);
updateObjectReferenceDescription(definedClass, getReference);
updateObjectReferenceFilter(definedClass, getReference);
updateObjectReferenceResolutionTime(definedClass, getReference);
createReferenceFluentEnd(objectReferenceOutline);
}
private void updateObjectReferenceType(JDefinedClass definedClass, JMethod getReference) {
JFieldVar typeField = definedClass.fields().get("type");
JMethod getType = recreateMethod(findMethod(definedClass, "getType"), definedClass);
copyAnnotations(getType, typeField);
JBlock body = getType.body();
body._return(JExpr.invoke(JExpr.invoke(getReference), "getTargetType"));
definedClass.removeField(typeField);
JMethod setType = recreateMethod(findMethod(definedClass, "setType"), definedClass);
body = setType.body();
JInvocation invocation = body.invoke(JExpr.invoke(getReference), "setTargetType");
invocation.arg(setType.listParams()[0]);
invocation.arg(JExpr.lit(true));
JFieldVar targetNameField = definedClass.fields().get("targetName");
JMethod getTargetName = recreateMethod(findMethod(definedClass, "getTargetName"), definedClass);
copyAnnotations(getTargetName, targetNameField);
JBlock getTargetNamebody = getTargetName.body();
JInvocation getTargetNameInvocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_REFERENCE_TARGET_NAME);
getTargetNameInvocation.arg(JExpr.invoke(getReference));
getTargetNamebody._return(getTargetNameInvocation);
definedClass.removeField(targetNameField);
JMethod setTargetName = recreateMethod(findMethod(definedClass, "setTargetName"), definedClass);
JBlock setTargetNamebody = setTargetName.body();
JInvocation setTagetNameInvocation = setTargetNamebody.staticInvoke(CLASS_MAP.get(PrismForJAXBUtil.class), METHOD_PRISM_UTIL_SET_REFERENCE_TARGET_NAME);
setTagetNameInvocation.arg(JExpr.invoke(getReference));
setTagetNameInvocation.arg(setTargetName.listParams()[0]);
}
private void updateObjectReferenceRelation(JDefinedClass definedClass, JMethod getReference) {
JFieldVar typeField = definedClass.fields().get("relation");
JMethod getType = recreateMethod(findMethod(definedClass, "getRelation"), definedClass);
copyAnnotations(getType, typeField);
JBlock body = getType.body();
body._return(JExpr.invoke(JExpr.invoke(getReference), "getRelation"));
definedClass.removeField(typeField);
JMethod setType = recreateMethod(findMethod(definedClass, "setRelation"), definedClass);
body = setType.body();
JInvocation invocation = body.invoke(JExpr.invoke(getReference), "setRelation");
invocation.arg(setType.listParams()[0]);
}
private void updateObjectReferenceOid(JDefinedClass definedClass, JMethod getReference) {
JFieldVar oidField = definedClass.fields().get("oid");
JMethod getOid = recreateMethod(findMethod(definedClass, "getOid"), definedClass);
copyAnnotations(getOid, oidField);
definedClass.removeField(oidField);
JBlock body = getOid.body();
body._return(JExpr.invoke(JExpr.invoke(getReference), getOid.name()));
JMethod setOid = recreateMethod(findMethod(definedClass, "setOid"), definedClass);
body = setOid.body();
JInvocation invocation = body.invoke(JExpr.invoke(getReference), setOid.name());
invocation.arg(setOid.listParams()[0]);
}
private void updateObjectReferenceDescription(JDefinedClass definedClass, JMethod getReference) {
JFieldVar descriptionField = definedClass.fields().get("description");
JMethod getDescription = recreateMethod(findMethod(definedClass, "getDescription"), definedClass);
copyAnnotations(getDescription, descriptionField);
definedClass.removeField(descriptionField);
JBlock body = getDescription.body();
body._return(JExpr.invoke(JExpr.invoke(getReference), getDescription.name()));
JMethod setDescription = recreateMethod(findMethod(definedClass, "setDescription"), definedClass);
body = setDescription.body();
JInvocation invocation = body.invoke(JExpr.invoke(getReference), setDescription.name());
invocation.arg(setDescription.listParams()[0]);
}
private void updateObjectReferenceFilter(JDefinedClass definedClass, JMethod asReferenceValue) {
JFieldVar filterField = definedClass.fields().get("filter");
JMethod getFilter = recreateMethod(findMethod(definedClass, "getFilter"), definedClass);
copyAnnotations(getFilter, filterField);
definedClass.removeField(filterField);
JBlock body = getFilter.body();
JType innerFilterType = getFilter.type();
JVar filterClassVar = body.decl(innerFilterType, "filter", JExpr._new(innerFilterType));
JInvocation getFilterElementInvocation =CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_REFERENCE_FILTER_CLAUSE_XNODE);
getFilterElementInvocation.arg(JExpr.invoke(asReferenceValue));
JInvocation setFilterInvocation = body.invoke(filterClassVar, "setFilterClauseXNode");
setFilterInvocation.arg(getFilterElementInvocation);
body._return(filterClassVar);
JMethod setFilter = recreateMethod(findMethod(definedClass, "setFilter"), definedClass);
body = setFilter.body();
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_SET_REFERENCE_FILTER_CLAUSE_XNODE);
invocation.arg(JExpr.invoke(asReferenceValue));
invocation.arg(setFilter.listParams()[0]);
body.add(invocation);
}
private void updateObjectReferenceResolutionTime(JDefinedClass definedClass, JMethod getReference) {
JFieldVar typeField = definedClass.fields().get("resolutionTime");
JMethod getType = recreateMethod(findMethod(definedClass, "getResolutionTime"), definedClass);
copyAnnotations(getType, typeField);
JBlock body = getType.body();
body._return(JExpr.invoke(JExpr.invoke(getReference), "getResolutionTime"));
definedClass.removeField(typeField);
JMethod setType = recreateMethod(findMethod(definedClass, "setResolutionTime"), definedClass);
body = setType.body();
JInvocation invocation = body.invoke(JExpr.invoke(getReference), "setResolutionTime");
invocation.arg(setType.listParams()[0]);
}
private JMethod findMethod(JDefinedClass definedClass, String methodName) {
for (JMethod method : definedClass.methods()) {
if (method.name().equals(methodName)) {
return method;
}
}
throw new IllegalArgumentException("Couldn't find method '" + methodName
+ "' in defined class '" + definedClass.name() + "'");
}
private Set<JDefinedClass> updatePrismContainer(Outline outline) {
Set<JDefinedClass> containers = new HashSet<JDefinedClass>();
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
ClassOutline classOutline = outline.getClazz(entry.getValue());
QName qname = getCClassInfoQName(entry.getValue());
if (qname == null || !hasAnnotation(classOutline, A_PRISM_CONTAINER)) {
continue;
}
if (hasAnnotation(classOutline, A_PRISM_OBJECT) && hasAnnotation(classOutline, A_PRISM_CONTAINER)) {
continue;
}
JDefinedClass definedClass = classOutline.implClass;
definedClass._implements(CLASS_MAP.get(Containerable.class));
containers.add(definedClass);
//inserting MidPointObject field into ObjectType class
JVar containerValue = definedClass.field(JMod.PRIVATE, PrismContainerValue.class, CONTAINER_VALUE_FIELD_NAME);
// default constructor
createDefaultConstructor(definedClass);
//create asPrismContainer
// createAsPrismContainer(classOutline, containerValue);
createAsPrismContainerValue(definedClass, containerValue);
//create setContainer
JMethod setupContainerMethod = createSetContainerValueMethod(definedClass, containerValue);
// constructor with prismContext
createPrismContextContainerableConstructor(definedClass, setupContainerMethod);
//create toString, equals, hashCode
createToStringMethod(definedClass, METHOD_AS_PRISM_CONTAINER_VALUE);
createEqualsMethod(classOutline, METHOD_AS_PRISM_CONTAINER_VALUE);
createHashCodeMethod(definedClass, METHOD_AS_PRISM_CONTAINER_VALUE);
//get container type
JMethod getContainerType = definedClass.method(JMod.NONE, QName.class, METHOD_GET_CONTAINER_TYPE);
// getContainerType.annotate(CLASS_MAP.get(XmlTransient.class));
JBlock body = getContainerType.body();
body._return(definedClass.staticRef(COMPLEX_TYPE_FIELD_NAME));
}
removeCustomGeneratedMethod(outline);
return containers;
}
private JMethod createDefaultConstructor(JDefinedClass definedClass) {
JMethod constructor = definedClass.constructor(JMod.PUBLIC);
constructor.body().invoke("super").invoke("aaa");
return constructor;
}
private JMethod createPrismContextContainerableConstructor(JDefinedClass definedClass, JMethod setupContainerMethod) {
JMethod constructor = definedClass.constructor(JMod.PUBLIC);
constructor.param(PrismContext.class, "prismContext");
JBlock body = constructor.body();
body.invoke(setupContainerMethod) // setupContainerValue(
.arg(JExpr._new(CLASS_MAP.get(PrismContainerValue.class).narrow(new JClass[0])) // new PrismContainerValue<>(
.arg(JExpr._this()) // this,
.arg(constructor.params().get(0))); // prismContext);
return constructor;
}
/*
public UserType(PrismContext prismContext) {
setupContainer(new PrismObject(_getContainerName(), this.getClass(), prismContext));
}
*/
private JMethod createPrismContextObjectableConstructor(JDefinedClass definedClass) {
JMethod constructor = definedClass.constructor(JMod.PUBLIC);
constructor.param(PrismContext.class, "prismContext");
JBlock body = constructor.body();
body.invoke("setupContainer")
.arg(JExpr._new(CLASS_MAP.get(PrismObject.class))
.arg(JExpr.invoke("_getContainerName"))
.arg(JExpr.invoke("getClass"))
.arg(constructor.params().get(0)));
return constructor;
}
private void createAsPrismContainerValueInObject(JDefinedClass definedClass) {
JMethod getContainer = definedClass.method(JMod.PUBLIC, CLASS_MAP.get(PrismContainerValue.class),
METHOD_AS_PRISM_CONTAINER_VALUE);
getContainer.annotate(CLASS_MAP.get(Override.class));
//create method body
JBlock body = getContainer.body();
body._return(JExpr.invoke(METHOD_AS_PRISM_CONTAINER).invoke(METHOD_CONTAINER_GET_VALUE));
}
private void createAsPrismContainerValue(JDefinedClass definedClass, JVar containerValueVar) {
JMethod getContainer = definedClass.method(JMod.PUBLIC, CLASS_MAP.get(PrismContainerValue.class),
METHOD_AS_PRISM_CONTAINER_VALUE);
// getContainer.annotate(CLASS_MAP.get(XmlTransient.class));
//create method body
JBlock body = getContainer.body();
body._if(containerValueVar.eq(JExpr._null())). // if (_containerValue == null) {
_then() //
.assign(containerValueVar, // _containerValue =
JExpr._new(CLASS_MAP.get(PrismContainerValue.class).narrow(new JClass[0])) // new PrismContainerValue<>(
.arg(JExpr._this()) // this)
);
body._return(containerValueVar);
}
private Set<JDefinedClass> updatePrismObject(Outline outline) {
Set<JDefinedClass> containers = new HashSet<JDefinedClass>();
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
ClassOutline classOutline = outline.getClazz(entry.getValue());
QName qname = getCClassInfoQName(entry.getValue());
if (qname == null) {
continue;
}
boolean isDirectPrismObject = hasAnnotation(classOutline, A_PRISM_OBJECT);
boolean isIndirectPrismObject = hasParentAnnotation(classOutline, A_PRISM_OBJECT);
if (!isIndirectPrismObject) {
continue;
}
JDefinedClass definedClass = classOutline.implClass;
createDefaultConstructor(definedClass);
createPrismContextObjectableConstructor(definedClass);
createAsPrismObject(definedClass);
if (!isDirectPrismObject) {
continue;
}
definedClass._implements(CLASS_MAP.get(Objectable.class));
containers.add(definedClass);
//inserting PrismObject field into ObjectType class
JVar container = definedClass.field(JMod.PRIVATE, PrismObject.class, CONTAINER_FIELD_NAME);
//create getContainer
// createGetContainerMethod(classOutline, container);
//create setContainer
createSetContainerMethod(definedClass, container);
//create asPrismObject()
createAsPrismContainer(classOutline, container);
// Objectable is also Containerable, we also need these
createAsPrismContainerValueInObject(definedClass);
createSetContainerValueMethodInObject(definedClass, container);
print("Creating toString, equals, hashCode methods.");
//create toString, equals, hashCode
createToStringMethod(definedClass, METHOD_AS_PRISM_CONTAINER);
createEqualsMethod(classOutline, METHOD_AS_PRISM_CONTAINER);
createHashCodeMethod(definedClass, METHOD_AS_PRISM_CONTAINER);
//create toDebugName, toDebugType
createToDebugName(definedClass);
createToDebugType(definedClass);
}
removeCustomGeneratedMethod(outline);
return containers;
}
/**
* Marks ObjectFactory.createXYZ methods for elements with a:rawType annotation as @Raw.
*/
private void updateObjectFactoryElements(Outline outline) {
XSSchemaSet schemaSet = outline.getModel().schemaComponent;
for (CElementInfo elementInfo : outline.getModel().getAllElements()) {
QName name = elementInfo.getElementName();
XSComponent elementDecl;
if (elementInfo.getSchemaComponent() != null) { // it's strange but elements seem not to have this filled-in...
elementDecl = elementInfo.getSchemaComponent();
} else {
elementDecl = schemaSet.getElementDecl(name.getNamespaceURI(), name.getLocalPart());
}
boolean isRaw = hasAnnotation(elementDecl, A_RAW_TYPE);
if (isRaw) {
print("*** Raw element found: " + elementInfo.getElementName());
JDefinedClass objectFactory = outline.getPackageContext(elementInfo._package()).objectFactory();
boolean methodFound = false; // finding method corresponding to the given element
for (JMethod method : objectFactory.methods()) {
for (JAnnotationUse annotationUse : method.annotations()) {
if (XmlElementDecl.class.getName().equals(annotationUse.getAnnotationClass().fullName())) {
// ugly method of finding the string value of the annotation members (couldn't find any better)
JAnnotationValue namespaceValue = annotationUse.getAnnotationMembers().get("namespace");
StringWriter namespaceWriter = new StringWriter();
JFormatter namespaceFormatter = new JFormatter(namespaceWriter);
namespaceValue.generate(namespaceFormatter);
JAnnotationValue nameValue = annotationUse.getAnnotationMembers().get("name");
StringWriter nameWriter = new StringWriter();
JFormatter nameFormatter = new JFormatter(nameWriter);
nameValue.generate(nameFormatter);
if (("\""+name.getNamespaceURI()+"\"").equals(namespaceWriter.toString()) &&
("\""+name.getLocalPart()+"\"").equals(nameWriter.toString())) {
print("*** Annotating method as @Raw: " + method.name());
method.annotate(Raw.class);
methodFound = true;
break;
}
}
}
}
if (!methodFound) {
throw new IllegalStateException("No factory method found for element " + name);
}
}
}
}
private void createAsPrismObject(JDefinedClass definedClass) {
JClass prismObjectClass = CLASS_MAP.get(PrismObject.class);
JType returnType;
if (definedClass.isAbstract()) {
returnType = prismObjectClass.narrow(definedClass.wildcard());
} else {
// e.g. PrismObject<TaskType> for TaskType
// we assume that we don't subclass a non-abstract object class into another one
returnType = prismObjectClass.narrow(definedClass);
}
JMethod asPrismObject = definedClass.method(JMod.PUBLIC, returnType, METHOD_AS_PRISM_OBJECT);
asPrismObject.annotate(CLASS_MAP.get(Override.class));
//create method body
JBlock body = asPrismObject.body();
body._return(JExpr.invoke(METHOD_AS_PRISM_CONTAINER));
}
private void updateClassAnnotation(ClassOutline classOutline) {
try {
JDefinedClass definedClass = classOutline.implClass;
List<JAnnotationUse> existingAnnotations = getAnnotations(definedClass);
for (JAnnotationUse annotation : existingAnnotations) {
if (isAnnotationTypeOf(annotation, XmlAccessorType.class)) {
Field field = getField(JAnnotationUse.class, "memberValues");
field.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) field.get(annotation);
field.setAccessible(false);
map.clear();
annotation.param("value", XmlAccessType.PROPERTY);
}
if (isAnnotationTypeOf(annotation, XmlType.class)) {
Field field = getField(JAnnotationUse.class, "memberValues");
field.setAccessible(true);
Map<String, Object> map = (Map<String, Object>) field.get(annotation);
Object propOrder = map.get("propOrder");
if (propOrder != null) {
JAnnotationArrayMember paramArray = (JAnnotationArrayMember)propOrder;
Field valField = getField(JAnnotationArrayMember.class, "values");
valField.setAccessible(true);
List<JAnnotationValue> values = (List<JAnnotationValue>) valField.get(paramArray);
for (int i=0; i < values.size(); i++) {
JAnnotationValue jAnnValue = values.get(i);
String value = extractString(jAnnValue);
if (value.startsWith("_")) {
paramArray.param(value.substring(1));
values.set(i, values.get(values.size() - 1));
values.remove(values.size() - 1);
}
// String valAfter = extractString(values.get(i));
// print("PPPPPPPPPPPPPPPPPPP: "+value+" -> "+valAfter);
}
valField.setAccessible(false);
}
field.setAccessible(false);
}
}
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
}
private String extractString(JAnnotationValue jAnnValue) {
StringWriter writer = new StringWriter();
JFormatter formatter = new JFormatter(writer);
jAnnValue.generate(formatter);
String value = writer.getBuffer().toString();
return value.substring(1, value.length() - 1);
}
private boolean isAnnotationTypeOf(JAnnotationUse annotation, Class clazz) {
try {
Field field = getField(JAnnotationUse.class, "clazz");
field.setAccessible(true);
JClass jClass = (JClass) field.get(annotation);
field.setAccessible(false);
if (CLASS_MAP.get(clazz).equals(jClass)) {
return true;
}
} catch (Exception ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
return false;
}
private void createToDebugName(JDefinedClass definedClass) {
JMethod method = definedClass.method(JMod.PUBLIC, String.class, "toDebugName");
method.annotate(CLASS_MAP.get(Override.class));
JBlock body = method.body();
JVar builder = body.decl(CLASS_MAP.get(StringBuilder.class), "builder",
JExpr._new(CLASS_MAP.get(StringBuilder.class)));
invokeAppendOnBuilder(body, builder, JExpr.dotclass(definedClass).invoke("getSimpleName"));
invokeAppendOnBuilder(body, builder, JExpr.lit("["));
invokeAppendOnBuilder(body, builder, JExpr.invoke("getOid"));
invokeAppendOnBuilder(body, builder, JExpr.lit(", "));
invokeAppendOnBuilder(body, builder, JExpr.invoke("getName"));
invokeAppendOnBuilder(body, builder, JExpr.lit("]"));
body._return(JExpr.invoke(builder, "toString"));
}
private void createToDebugType(JDefinedClass definedClass) {
JMethod method = definedClass.method(JMod.PUBLIC, String.class, "toDebugType");
method.annotate(CLASS_MAP.get(Override.class));
JBlock body = method.body();
JVar builder = body.decl(CLASS_MAP.get(StringBuilder.class), "builder",
JExpr._new(CLASS_MAP.get(StringBuilder.class)));
invokeAppendOnBuilder(body, builder, JExpr.dotclass(definedClass).invoke("getSimpleName"));
body._return(JExpr.invoke(builder, "toString"));
}
private void invokeAppendOnBuilder(JBlock body, JVar builder, JExpression expression) {
JInvocation invocation = body.invoke(builder, "append");
invocation.arg(expression);
}
private void createHashCodeMethod(JDefinedClass definedClass, String baseMethodName) {
JMethod hashCode = definedClass.getMethod(METHOD_HASH_CODE, new JType[]{});
if (hashCode == null) {
hashCode = definedClass.method(JMod.PUBLIC, int.class, METHOD_HASH_CODE);
} else {
hashCode = recreateMethod(hashCode, definedClass);
}
hashCode.annotate(CLASS_MAP.get(Override.class));
JBlock body = hashCode.body();
body._return(JExpr.invoke(baseMethodName).invoke(METHOD_HASH_CODE));
}
/**
* remove generated equals methods from classes which extends from prism containers/objects
*/
private void removeCustomGeneratedMethod(Outline outline) {
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
ClassOutline classOutline = outline.getClazz(entry.getValue());
QName qname = getCClassInfoQName(entry.getValue());
if (qname == null || (!hasParentAnnotation(classOutline, A_PRISM_OBJECT)
&& !hasParentAnnotation(classOutline, A_PRISM_CONTAINER))) {
continue;
}
JDefinedClass definedClass = classOutline.implClass;
Iterator<JClass> iterator = definedClass._implements();
while (iterator.hasNext()) {
JClass clazz = iterator.next();
if (clazz.equals(CLASS_MAP.get(Equals.class)) || clazz.equals(CLASS_MAP.get(HashCode.class))) {
iterator.remove();
}
}
boolean isMidpointContainer = hasParentAnnotation(classOutline, A_PRISM_OBJECT);
removeOldCustomGeneratedEquals(classOutline, isMidpointContainer);
removeOldCustomGenerated(classOutline, isMidpointContainer, METHOD_HASH_CODE);
removeOldCustomGenerated(classOutline, isMidpointContainer, METHOD_TO_STRING);
}
}
private void removeOldCustomGenerated(ClassOutline classOutline, boolean isPrismObject, String methodName) {
JDefinedClass definedClass = classOutline.implClass;
Iterator<JMethod> methods = definedClass.methods().iterator();
while (methods.hasNext()) {
JMethod method = methods.next();
if (isPrismObject && !hasAnnotation(classOutline, A_PRISM_OBJECT)) {
if (method.name().equals(methodName)) {
methods.remove();
}
} else {
if (method.name().equals(methodName) && method.listParams().length != 0) {
methods.remove();
}
}
}
}
private void removeOldCustomGeneratedEquals(ClassOutline classOutline, boolean isPrismObject) {
JDefinedClass definedClass = classOutline.implClass;
Iterator<JMethod> methods = definedClass.methods().iterator();
while (methods.hasNext()) {
JMethod method = methods.next();
if (isPrismObject && !hasAnnotation(classOutline, A_PRISM_OBJECT)) {
if (method.name().equals(METHOD_EQUALS)) {
methods.remove();
}
} else {
if (method.name().equals(METHOD_EQUALS) && method.listParams().length != 1) {
methods.remove();
}
}
}
}
private void createEqualsMethod(ClassOutline classOutline, String baseMethod) {
JDefinedClass definedClass = classOutline.implClass;
JMethod equals = definedClass.getMethod(METHOD_EQUALS, new JType[]{CLASS_MAP.get(Object.class)});
if (equals != null) {
// removeOldCustomGeneratedEquals(classOutline, hasParentAnnotation(classOutline, PRISM_OBJECT)); todo can this be removed?
equals = recreateMethod(equals, definedClass);
} else {
equals = definedClass.method(JMod.PUBLIC, boolean.class, METHOD_EQUALS);
}
equals.annotate(CLASS_MAP.get(Override.class));
JBlock body = equals.body();
JVar obj = equals.listParams()[0];
JBlock ifNull = body._if(obj._instanceof(definedClass).not())._then();
ifNull._return(JExpr.lit(false));
JVar other = body.decl(definedClass, "other", JExpr.cast(definedClass, obj));
JInvocation invocation = JExpr.invoke(baseMethod).invoke(METHOD_EQUIVALENT);
invocation.arg(other.invoke(baseMethod));
body._return(invocation);
}
private void createToStringMethod(JDefinedClass definedClass, String baseMethod) {
JMethod toString = definedClass.getMethod("toString", new JType[]{});
if (toString == null) {
toString = definedClass.method(JMod.PUBLIC, CLASS_MAP.get(String.class), METHOD_TO_STRING);
} else {
toString = recreateMethod(toString, definedClass);
}
toString.annotate(CLASS_MAP.get(Override.class));
JBlock body = toString.body();
JInvocation invocation = JExpr.invoke(baseMethod).invoke(METHOD_TO_STRING);
body._return(invocation);
}
private JMethod createSetContainerValueMethod(JDefinedClass definedClass, JVar container) {
JMethod setContainer = definedClass.method(JMod.PUBLIC, void.class, METHOD_SETUP_CONTAINER_VALUE);
JVar methodContainer = setContainer.param(PrismContainerValue.class, "containerValue");
//create method body
JBlock body = setContainer.body();
// JBlock then = body._if(methodContainer.eq(JExpr._null()))._then();
// then.assign(JExpr._this().ref(container), JExpr._null());
// then._return();
//
// then = body._if(methodContainer.invoke("getParent").eq(JExpr._null()))._then();
// then.assign(JExpr._this().ref(container), methodContainer);
// then._return();
//
// JVar definition = body.decl(CLASS_MAP.get(PrismContainerDefinition.class), "definition",
// JExpr.invoke(JExpr.invoke(methodContainer, "getParent"), "getDefinition"));
//
// JInvocation equals = JExpr.invoke(JExpr.invoke(METHOD_GET_CONTAINER_TYPE), "equals");
// equals.arg(definition.invoke("getTypeName"));
// then = body._if(definition.ne(JExpr._null()).cand(equals.not()))._then();
// JInvocation exception = JExpr._new(CLASS_MAP.get(IllegalArgumentException.class));
//
// JExpression message = JExpr.lit("Container definition type qname '").plus(JExpr.invoke(definition, "getTypeName"))
// .plus(JExpr.lit("' doesn't equals to '")).plus(JExpr.invoke(METHOD_GET_CONTAINER_TYPE))
// .plus(JExpr.lit("'."));
// exception.arg(message);
// then._throw(exception);
body.assign(JExpr._this().ref(container), methodContainer);
return setContainer;
}
private void createSetContainerValueMethodInObject(JDefinedClass definedClass, JVar container) {
JMethod setContainerValue = definedClass.method(JMod.PUBLIC, void.class, METHOD_SETUP_CONTAINER_VALUE);
setContainerValue.annotate(CLASS_MAP.get(Override.class));
JVar containerValue = setContainerValue.param(PrismContainerValue.class, "containerValue");
//create method body
JBlock body = setContainerValue.body();
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_SETUP_CONTAINER_VALUE);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER));
invocation.arg(containerValue);
body.assign(container, invocation);
}
private void createAsPrismContainer(ClassOutline classOutline, JVar container) {
JDefinedClass definedClass = classOutline.implClass;
JMethod getContainer = definedClass.method(JMod.PUBLIC, CLASS_MAP.get(PrismObject.class),
METHOD_AS_PRISM_CONTAINER);
//create method body
JBlock body = getContainer.body();
JBlock then = body._if(container.eq(JExpr._null()))._then();
JInvocation newContainer = JExpr._new(CLASS_MAP.get(PrismObject.class));
newContainer.arg(JExpr.invoke(METHOD_GET_CONTAINER_NAME));
newContainer.arg(JExpr._this().invoke("getClass"));
// newContainer.arg(JExpr.dotclass(definedClass));
then.assign(container, newContainer);
body._return(container);
}
private JMethod createSetContainerMethod(JDefinedClass definedClass, JVar container) {
JMethod setContainer = definedClass.method(JMod.PUBLIC, void.class, METHOD_SETUP_CONTAINER);
JVar methodContainer = setContainer.param(PrismObject.class, "container");
//create method body
JBlock body = setContainer.body();
// JBlock then = body._if(methodContainer.eq(JExpr._null()))._then();
// then.assign(JExpr._this().ref(container), JExpr._null());
// then._return();
//
// JVar definition = body.decl(CLASS_MAP.get(PrismContainerDefinition.class), "definition",
// JExpr.invoke(methodContainer, "getDefinition"));
//
// JInvocation equals = JExpr.invoke(JExpr.invoke(METHOD_GET_CONTAINER_TYPE), "equals");
// equals.arg(definition.invoke("getTypeName"));
// then = body._if(definition.ne(JExpr._null()).cand(equals.not()))._then();
// JInvocation exception = JExpr._new(CLASS_MAP.get(IllegalArgumentException.class));
//
// JExpression message = JExpr.lit("Container definition type qname '").plus(JExpr.invoke(definition, "getTypeName"))
// .plus(JExpr.lit("' doesn't equals to '")).plus(JExpr.invoke(METHOD_GET_CONTAINER_TYPE))
// .plus(JExpr.lit("'."));
// exception.arg(message);
// then._throw(exception);
body.assign(JExpr._this().ref(container), methodContainer);
return setContainer;
}
private QName getCClassInfoQName(CClassInfo info) {
QName qname = info.getTypeName();
if (qname == null) {
qname = info.getElementName();
}
return qname;
}
private void addContainerName(Outline outline, Map<String, JFieldVar> namespaceFields) {
Map<QName, List<QName>> complexTypeToElementName = null;
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
CClassInfo classInfo = entry.getValue();
ClassOutline classOutline = outline.getClazz(classInfo);
if (complexTypeToElementName == null) {
complexTypeToElementName = getComplexTypeToElementName(classOutline);
}
QName qname = getCClassInfoQName(classInfo);
if (qname == null || !hasParentAnnotation(classOutline, A_PRISM_OBJECT)) {
continue;
}
//element name
List<QName> qnames = complexTypeToElementName.get(qname);
if (qnames == null || qnames.size() != 1) {
printWarning("Found zero or more than one element names for type '"
+ qname + "', " + qnames + ".");
continue;
}
qname = qnames.get(0);
JDefinedClass definedClass = classOutline.implClass;
JMethod getContainerName = definedClass.method(JMod.NONE, QName.class, METHOD_GET_CONTAINER_NAME);
// getContainerName.annotate(CLASS_MAP.get(XmlTransient.class));
JBlock body = getContainerName.body();
JFieldVar var = namespaceFields.get(qname.getNamespaceURI());
JInvocation invocation = JExpr._new(CLASS_MAP.get(QName.class));
if (var != null) {
JClass schemaClass = outline.getModel().codeModel._getClass(StepSchemaConstants.CLASS_NAME);
invocation.arg(schemaClass.staticRef(var));
invocation.arg(qname.getLocalPart());
} else {
invocation.arg(qname.getNamespaceURI());
invocation.arg(qname.getLocalPart());
}
body._return(invocation);
//get container type
JMethod getContainerType = definedClass.method(JMod.NONE, QName.class, METHOD_GET_CONTAINER_TYPE);
// getContainerType.annotate(CLASS_MAP.get(XmlTransient.class));
body = getContainerType.body();
body._return(definedClass.staticRef(COMPLEX_TYPE_FIELD_NAME));
}
}
private boolean hasParentAnnotation(ClassOutline classOutline, QName annotation) {
if (classOutline.getSuperClass() == null) {
return hasAnnotation(classOutline, annotation);
}
return hasAnnotation(classOutline, annotation) || hasParentAnnotation(classOutline.getSuperClass(), annotation);
}
private void addComplextType(Outline outline, Map<String, JFieldVar> namespaceFields) {
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
ClassOutline classOutline = outline.getClazz(entry.getValue());
QName qname = entry.getValue().getTypeName();
if (qname == null) {
continue;
}
JFieldVar var = namespaceFields.get(qname.getNamespaceURI());
if (var != null) {
createQNameDefinition(outline, classOutline.implClass, COMPLEX_TYPE_FIELD_NAME, var, qname);
} else {
createPSFField(outline, classOutline.implClass, COMPLEX_TYPE_FIELD_NAME, qname);
}
}
}
private Map<QName, List<QName>> getComplexTypeToElementName(ClassOutline classOutline) {
Map<QName, List<QName>> complexTypeToElementName = new HashMap<QName, List<QName>>();
XSSchemaSet schemaSet = classOutline.target.getSchemaComponent().getRoot();
for (XSSchema schema : schemaSet.getSchemas()) {
Map<String, XSElementDecl> elemDecls = schema.getElementDecls();
for (Entry<String, XSElementDecl> entry : elemDecls.entrySet()) {
XSElementDecl decl = entry.getValue();
XSType xsType = decl.getType();
if (xsType.getName() == null) {
continue;
}
QName type = new QName(xsType.getTargetNamespace(), xsType.getName());
List<QName> qnames = complexTypeToElementName.get(type);
if (qnames == null) {
qnames = new ArrayList<QName>();
complexTypeToElementName.put(type, qnames);
}
qnames.add(new QName(decl.getTargetNamespace(), decl.getName()));
}
}
return complexTypeToElementName;
}
private JFieldVar createQNameDefinition(Outline outline, JDefinedClass definedClass, String fieldName,
JFieldVar namespaceField, QName reference) {
JClass schemaClass = outline.getModel().codeModel._getClass(StepSchemaConstants.CLASS_NAME);
JInvocation invocation = JExpr._new(CLASS_MAP.get(QName.class));
invocation.arg(schemaClass.staticRef(namespaceField));
invocation.arg(reference.getLocalPart());
int psf = JMod.PUBLIC | JMod.STATIC | JMod.FINAL;
return definedClass.field(psf, QName.class, fieldName, invocation);
}
private void addFieldQNames(Outline outline, Map<String, JFieldVar> namespaceFields) {
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
ClassOutline classOutline = outline.getClazz(entry.getValue());
QName qname = getCClassInfoQName(entry.getValue());
if (qname == null) {
continue;
}
JDefinedClass implClass = classOutline.implClass;
Map<String, JFieldVar> fields = implClass.fields();
if (fields == null) {
continue;
}
boolean isObject = hasAnnotation(classOutline, A_PRISM_OBJECT);
List<FieldBox<QName>> boxes = new ArrayList<FieldBox<QName>>();
for (Entry<String, JFieldVar> fieldEntry : fields.entrySet()) {
String field = normalizeFieldName(fieldEntry.getKey());
if ((isObject && ("oid".equals(field) || "version".equals(field)) ||
"serialVersionUID".equals(field) || "id".equals(field) || COMPLEX_TYPE_FIELD_NAME.equals(field))) {
continue;
}
if (hasAnnotationClass(fieldEntry.getValue(), XmlAnyElement.class)) {
continue;
}
String fieldName = fieldFPrefixUnderscoredUpperCase(field);
boxes.add(new FieldBox<>(fieldName, new QName(qname.getNamespaceURI(), field)));
}
for (FieldBox<QName> box : boxes) {
JFieldVar var = namespaceFields.get(qname.getNamespaceURI());
if (var != null) {
createQNameDefinition(outline, implClass, box.getFieldName(), var, box.getValue());
} else {
createPSFField(outline, implClass, box.getFieldName(), box.getValue());
}
}
}
}
private void updateFields(Outline outline) {
Map<JDefinedClass, List<JFieldVar>> allFieldsToBeRemoved = new HashMap<>();
Set<Map.Entry<NClass, CClassInfo>> set = outline.getModel().beans().entrySet();
for (Map.Entry<NClass, CClassInfo> entry : set) {
ClassOutline classOutline = outline.getClazz(entry.getValue());
JDefinedClass implClass = classOutline.implClass;
Map<String, JFieldVar> fields = implClass.fields();
if (fields == null) {
continue;
}
print("Updating fields and get/set methods: " + classOutline.implClass.fullName());
for (String field : fields.keySet()) {
JFieldVar fieldVar = fields.get(field);
// marks a:rawType fields with @Raw - this has to be executed for any bean, not only for prism containers
if (hasAnnotation(classOutline, fieldVar, A_RAW_TYPE) != null) {
annotateFieldAsRaw(fieldVar);
}
}
if (isContainer(classOutline.implClass, outline)) {
processContainerFields(classOutline, allFieldsToBeRemoved);
}
createFluentFieldMethods(classOutline, classOutline);
print("Finished updating fields and get/set methods for " + classOutline.implClass.fullName());
}
allFieldsToBeRemoved.forEach((jDefinedClass, jFieldVars) -> {
jFieldVars.forEach(field -> jDefinedClass.removeField(field));
});
}
private void processContainerFields(ClassOutline classOutline, Map<JDefinedClass, List<JFieldVar>> allFieldsToBeRemoved) {
JDefinedClass implClass = classOutline.implClass;
Map<String, JFieldVar> fields = implClass.fields();
createContainerFluentEnd(classOutline); // not available for beans (no parent there)
updateClassAnnotation(classOutline);
boolean isObject = hasAnnotation(classOutline, A_PRISM_OBJECT);
List<JFieldVar> fieldsToBeRemoved = new ArrayList<>();
for (String field : fields.keySet()) {
JFieldVar fieldVar = fields.get(field);
if (isAuxiliaryField(fieldVar)) {
continue;
}
boolean remove;
if (isObject && ("oid".equals(field) || "version".equals(field))) {
print("Updating simple field: " + fieldVar.name());
remove = updateSimpleField(fieldVar, classOutline, METHOD_AS_PRISM_CONTAINER);
} else if ("id".equals(field)) {
print("Updating container id field: " + fieldVar.name());
remove = updateIdField(fieldVar, classOutline);
} else if (isFieldReference(fieldVar, classOutline)) {
print("Updating field (reference): " + fieldVar.name());
remove = updateFieldReference(fieldVar, classOutline);
} else if (isFieldReferenceUse(fieldVar, classOutline)) {
print("Updating field (reference usage): " + fieldVar.name());
remove = updateFieldReferenceUse(fieldVar, classOutline);
} else if (isFieldTypeContainer(fieldVar, classOutline)) {
print("Updating container field: " + fieldVar.name());
remove = updateContainerFieldType(fieldVar, classOutline);
} else {
print("Updating field: " + fieldVar.name());
remove = updateField(fieldVar, classOutline);
}
if (remove) {
fieldsToBeRemoved.add(fieldVar);
}
}
allFieldsToBeRemoved.put(implClass, fieldsToBeRemoved);
}
private void createFluentFieldMethods(ClassOutline targetClass, ClassOutline sourceClass) {
Map<String, JFieldVar> fields = sourceClass.implClass.fields();
for (String field : fields.keySet()) {
JFieldVar fieldVar = fields.get(field);
if (!isAuxiliaryField(fieldVar) && !hasAnnotationClass(fieldVar, XmlAnyElement.class)) {
createFluentFieldMethods(fieldVar, targetClass, sourceClass);
}
}
if (sourceClass.getSuperClass() != null) {
createFluentFieldMethods(targetClass, sourceClass.getSuperClass());
}
}
private boolean isAuxiliaryField(JFieldVar fieldVar) {
String field = fieldVar.name();
return "serialVersionUID".equals(field) || COMPLEX_TYPE_FIELD_NAME.equals(field)
|| CONTAINER_FIELD_NAME.equals(field) || CONTAINER_VALUE_FIELD_NAME.equals(field)
|| "otherAttributes".equals(field) && fieldVar.type().name().equals("Map<QName,String>")
|| isFField(fieldVar);
}
private boolean isFField(JFieldVar fieldVar) {
boolean isPublicStaticFinal = (fieldVar.mods().getValue() & (JMod.STATIC | JMod.FINAL)) != 0;
if (fieldVar.name().startsWith("F_") && isPublicStaticFinal) {
//our QName constant fields
return true;
}
return false;
}
private boolean updateIdField(JFieldVar field, ClassOutline classOutline) {
JMethod method = recreateGetter(field, classOutline);
JBlock body = method.body();
body._return(JExpr.invoke(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE), "getId"));
method = recreateSetter(field, classOutline);
body = method.body();
JInvocation invocation = body.invoke(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE), "setId");
invocation.arg(method.listParams()[0]);
return true;
}
private JMethod recreateSetter(JFieldVar field, ClassOutline classOutline) {
JDefinedClass definedClass = classOutline.implClass;
JMethod method = findSetterMethod(field, classOutline);
return recreateMethod(method, definedClass);
}
private JMethod findSetterMethod(JFieldVar field, ClassOutline classOutline) {
String methodName = getSetterMethodName(classOutline, field);
return classOutline.implClass.getMethod(methodName, new JType[]{field.type()});
}
private JMethod recreateGetter(JFieldVar field, ClassOutline classOutline) {
JDefinedClass definedClass = classOutline.implClass;
JMethod method = findGetterMethod(field, classOutline);
JMethod newGetter = recreateMethod(method, definedClass);
copyAnnotations(newGetter, field);
return newGetter;
}
private JMethod findGetterMethod(JFieldVar field, ClassOutline classOutline) {
String methodName = getGetterMethodName(classOutline, field);
return classOutline.implClass.getMethod(methodName, new JType[]{});
}
private boolean updateFieldReference(JFieldVar field, ClassOutline classOutline) {
JMethod getterMethod = recreateGetter(field, classOutline);
annotateMethodWithXmlElement(getterMethod, field);
boolean isList = isList(field.type());
createFieldReferenceGetterBody(field, classOutline, getterMethod.body(), isList);
//setter method update
if (!isList) {
JMethod setterMethod = recreateSetter(field, classOutline);
JVar param = setterMethod.listParams()[0];
createFieldReferenceSetterBody(field, param, setterMethod.body());
} else {
createFieldListCreator(field, classOutline, getterMethod, "createReference");
}
return true;
}
private void createFieldReferenceSetterBody(JFieldVar field, JVar param, JBlock body) {
JVar cont = body.decl(CLASS_MAP.get(PrismReferenceValue.class), REFERENCE_VALUE_FIELD_NAME,
JOp.cond(param.ne(JExpr._null()), JExpr.invoke(param, METHOD_AS_REFERENCE_VALUE), JExpr._null()));
JInvocation invocation = body.staticInvoke(CLASS_MAP.get(PrismForJAXBUtil.class),
METHOD_PRISM_UTIL_SET_REFERENCE_VALUE_AS_REF);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())));
invocation.arg(cont);
}
// todo reimplement, now we're using inner classes
// JDefinedClass annonymous = outline.getCodeModel().anonymousClass(clazz);
// annonymous.hide();
private JDefinedClass createFieldReferenceGetterListAnon(JFieldVar field, ClassOutline classOutline) {
//add generics type to list field.type.getTypeParameters()...
JClass type = ((JClass) field.type()).getTypeParameters().get(0);
JClass clazz = CLASS_MAP.get(PrismReferenceArrayList.class).narrow(type);
JDefinedClass anonymous = createAnonListClass(field, classOutline);
anonymous._implements(Serializable.class);
anonymous._extends(clazz);
JMethod constructor = anonymous.constructor(JMod.PUBLIC);
constructor.param(CLASS_MAP.get(PrismReference.class), REFERENCE_LOCAL_VARIABLE_NAME);
constructor.param(CLASS_MAP.get(PrismContainerValue.class), "parent");
JBlock constructorBody = constructor.body();
JInvocation invocation = constructorBody.invoke("super");
invocation.arg(constructor.listParams()[0]);
invocation.arg(constructor.listParams()[1]);
JMethod createItem = anonymous.method(JMod.PROTECTED, type, "createItem");
createItem.annotate(CLASS_MAP.get(Override.class));
createItem.param(CLASS_MAP.get(PrismReferenceValue.class), "value");
JMethod getValueFrom = anonymous.method(JMod.PROTECTED, CLASS_MAP.get(PrismReferenceValue.class), "getValueFrom");
getValueFrom.annotate(CLASS_MAP.get(Override.class));
getValueFrom.param(type, "value");
JMethod willClear = anonymous.method(JMod.PROTECTED, boolean.class, "willClear");
willClear.annotate(CLASS_MAP.get(Override.class));
willClear.param(CLASS_MAP.get(PrismReferenceValue.class), "value");
return anonymous;
}
@NotNull
private JDefinedClass createAnonListClass(JFieldVar field, ClassOutline classOutline) {
JDefinedClass anonymous;
try {
CPropertyInfo propertyInfo = classOutline.target.getProperty(field.name());
anonymous = classOutline.implClass._class(JMod.PRIVATE | JMod.STATIC, "Anon" + propertyInfo.getName(true));
JDocComment comment = anonymous.javadoc();
comment.append("TODO Can't be anonymous because of NPE bug in CodeModel generator, will be fixed later.");
} catch (JClassAlreadyExistsException ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
return anonymous;
}
private void createFieldReferenceCreateItemBody(JFieldVar field, JMethod method) {
JClass type = ((JClass) field.type()).getTypeParameters().get(0);
JBlock body = method.body();
JExpression initExpr;
initExpr = constructorExpression(method, type);
JVar decl = body.decl(type, field.name(), initExpr);
JInvocation invocation = body.invoke(decl, METHOD_SETUP_REFERENCE_VALUE);
invocation.arg(method.listParams()[0]);
body._return(decl);
}
private JExpression constructorExpression(JMethod method, JClass type) {
JExpression initExpr;
if (type.isAbstract()) {
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_CREATE_TARGET_INSTANCE);
invocation.arg(method.listParams()[0]);
initExpr = invocation;
} else {
initExpr = JExpr._new(type);
}
return initExpr;
}
private void createFieldReferenceGetValueFrom(JFieldVar field, JMethod method) {
JBlock body = method.body();
body._return(JExpr.invoke(method.listParams()[0], METHOD_AS_REFERENCE_VALUE));
}
private void createFieldReferenceWillClear(JFieldVar field, JMethod method) {
JBlock body = method.body();
JInvocation getObject = JExpr.invoke(method.listParams()[0], "getObject");
body._return(getObject.eq(JExpr._null()));
}
private void createFieldReferenceUseWillClear(JFieldVar field, JMethod method) {
JBlock body = method.body();
JInvocation getObject = JExpr.invoke(method.listParams()[0], "getObject");
body._return(getObject.ne(JExpr._null()));
}
private void createFieldReferenceGetterBody(JFieldVar field, ClassOutline classOutline, JBlock body,
boolean isList) {
JFieldRef qnameRef = JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name()));
if (isList) {
//if it's List<ObjectReferenceType> ...
// PrismContainerValue pcv = asPrismContainerValue();
JVar pcv = body.decl(CLASS_MAP.get(PrismContainerValue.class), "pcv", JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
// PrismReference reference = PrismForJAXBUtil.getReference(pcv, F_LINK_REF);
JInvocation invoke = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_REFERENCE);
invoke.arg(pcv);
invoke.arg(qnameRef);
JVar reference = body.decl(CLASS_MAP.get(PrismReference.class), REFERENCE_LOCAL_VARIABLE_NAME, invoke);
// FocusType.AnonLinkRef + its methods
JDefinedClass anonymous = createFieldReferenceGetterListAnon(field, classOutline);
createFieldReferenceCreateItemBody(field, findMethod(anonymous, "createItem"));
createFieldReferenceGetValueFrom(field, findMethod(anonymous, "getValueFrom"));
createFieldReferenceWillClear(field, findMethod(anonymous, "willClear"));
// return new FocusType.AnonLinkRef(reference, pcv);
JInvocation newList = JExpr._new(anonymous);
newList.arg(reference);
newList.arg(pcv);
body._return(newList);
} else {
//if it's ObjectReferenceType
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_REFERENCE_VALUE);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(qnameRef);
JVar container = body.decl(CLASS_MAP.get(PrismReferenceValue.class), REFERENCE_LOCAL_VARIABLE_NAME, invocation);
JBlock then = body._if(container.eq(JExpr._null()))._then();
then._return(JExpr._null());
JVar wrapper = body.decl(field.type(), field.name(), JExpr._new(field.type()));
invocation = body.invoke(wrapper, METHOD_SETUP_REFERENCE_VALUE);
invocation.arg(container);
body._return(wrapper);
}
}
private JFieldVar getReferencedField(JFieldVar field, ClassOutline classOutline) {
QName qname = getFieldReferenceUseAnnotationQName(field, classOutline);
CPropertyInfo propertyInfo = classOutline.target.getProperty(qname.getLocalPart());
if (propertyInfo == null) {
throw new IllegalArgumentException("No property "+qname.getLocalPart()+" in "+classOutline.target);
}
return classOutline.implClass.fields().get(propertyInfo.getName(false));
}
// e.g. c:link (as opposed to c:linkRef)
private boolean updateFieldReferenceUse(JFieldVar field, ClassOutline classOutline) {
//getter method update
JMethod getterMethod = recreateGetter(field, classOutline);
annotateMethodWithXmlElement(getterMethod, field);
boolean isList = isList(field.type());
createFieldReferenceUseGetterBody(field, classOutline, getterMethod.body(), isList);
//setter method update
if (!isList) {
JMethod setterMethod = recreateSetter(field, classOutline);
createFieldReferenceUseSetterBody(field, classOutline, setterMethod.listParams()[0], setterMethod.body());
} else {
createFieldListCreator(field, classOutline, getterMethod, "createReference");
}
return true;
}
private void createFieldReferenceUseSetterBody(JFieldVar field, ClassOutline classOutline, JVar param,
JBlock body) {
JVar cont = body.decl(CLASS_MAP.get(PrismObject.class), OBJECT_LOCAL_FIELD_NAME, JOp.cond(param.ne(JExpr._null()),
JExpr.invoke(param, METHOD_AS_PRISM_CONTAINER), JExpr._null()));
JInvocation invocation = body.staticInvoke(CLASS_MAP.get(PrismForJAXBUtil.class),
METHOD_PRISM_UTIL_SET_REFERENCE_VALUE_AS_OBJECT);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
JFieldVar referencedField = getReferencedField(field, classOutline);
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(referencedField.name())));
invocation.arg(cont);
}
private void createFieldReferenceUseCreateItemBody(JFieldVar field, JMethod method) {
JClass type = ((JClass) field.type()).getTypeParameters().get(0);
JBlock body = method.body();
JExpression initExpr;
initExpr = constructorExpression(method, type);
JVar decl = body.decl(type, field.name(), initExpr);
JInvocation invocation = body.invoke(decl, METHOD_SETUP_CONTAINER);
invocation.arg(JExpr.invoke(method.listParams()[0], "getObject"));
body._return(decl);
}
private void createFieldReferenceUseGetValueFrom(JFieldVar field, JMethod method) {
JBlock body = method.body();
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_OBJECTABLE_AS_REFERENCE_VALUE);
invocation.arg(method.listParams()[0]);
invocation.arg(JExpr.invoke("getReference"));
body._return(invocation);
// JVar object = body.decl(CLASS_MAP.get(PrismObject.class), "object",
// JExpr.invoke(method.listParams()[0], METHOD_AS_PRISM_OBJECT));
// JVar reference = body.decl(CLASS_MAP.get(PrismReference.class), "reference",
// JExpr.invoke("getReference"));
// JForEach forEach = body.forEach(CLASS_MAP.get(PrismReferenceValue.class), "refValue",
// JExpr.invoke(reference, "getValues"));
// JBlock forBody = forEach.body();
// JBlock then = forBody._if(object.eq(JExpr.invoke(forEach.var(), "getObject")))._then();
// then._return(forEach.var());
// body._return(JExpr._null());
}
private void createFieldReferenceUseGetterBody(JFieldVar field, ClassOutline classOutline, JBlock body,
boolean isList) {
JFieldVar refField = getReferencedField(field, classOutline);
JFieldRef qnameRef = JExpr.ref(fieldFPrefixUnderscoredUpperCase(refField.name()));
if (isList) {
// PrismContainerValue pcv = asPrismContainerValue()
JVar pcv = body.decl(CLASS_MAP.get(PrismContainerValue.class), "pcv", JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
// PrismReference reference = PrismForJAXBUtil.getReference(pcv, F_LINK_REF);
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_REFERENCE);
invocation.arg(pcv);
invocation.arg(qnameRef);
JVar reference = body.decl(CLASS_MAP.get(PrismReference.class), REFERENCE_LOCAL_VARIABLE_NAME, invocation);
// anonymous class (e.g. FocusType.AnonLink) and its methods
JDefinedClass anonymous = createFieldReferenceGetterListAnon(field, classOutline);
createFieldReferenceUseCreateItemBody(field, findMethod(anonymous, "createItem"));
createFieldReferenceUseGetValueFrom(field, findMethod(anonymous, "getValueFrom"));
createFieldReferenceUseWillClear(field, findMethod(anonymous, "willClear"));
// return new FocusType.AnonLinkRef(reference, pcv);
JInvocation newList = JExpr._new(anonymous);
newList.arg(reference);
newList.arg(pcv);
body._return(newList);
} else {
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_REFERENCE_VALUE);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(qnameRef);
JVar reference = body.decl(CLASS_MAP.get(PrismReferenceValue.class), REFERENCE_LOCAL_VARIABLE_NAME, invocation);
JBlock then = body._if(reference.eq(JExpr._null()).cor(JExpr.invoke(reference, "getObject")
.eq(JExpr._null())))._then();
then._return(JExpr._null());
body._return(JExpr.cast(field.type(), JExpr.invoke(reference, "getObject").invoke("asObjectable")));
}
}
private boolean isFieldReference(JFieldVar field, ClassOutline classOutline) {
CPropertyInfo propertyInfo = classOutline.target.getProperty(field.name());
Collection<? extends CTypeInfo> collection = propertyInfo.ref();
if (collection == null || collection.isEmpty()) {
return false;
}
CTypeInfo info = collection.iterator().next();
if (info instanceof CClassInfo) {
CClassInfo classInfo = (CClassInfo) info;
if (OBJECT_REFERENCE_TYPE.equals(classInfo.getTypeName())) {
return true;
}
}
return false;
}
// e.g. c:link (as opposed to c:linkRef)
private QName getFieldReferenceUseAnnotationQName(JFieldVar field, ClassOutline classOutline) {
BIDeclaration declaration = hasAnnotation(classOutline, field, A_OBJECT_REFERENCE);
if (!(declaration instanceof BIXPluginCustomization)) {
return null;
}
BIXPluginCustomization customization = (BIXPluginCustomization) declaration;
if (customization.element == null) {
return null;
}
Element element = customization.element;
String strQName = element.getTextContent();
String[] array = strQName.split(":");
if (array.length == 2) {
return new QName(PrefixMapper.C.getNamespace(), array[1]);
} else if (array.length == 1) {
return new QName(PrefixMapper.C.getNamespace(), array[0]);
}
return null;
}
private boolean isFieldReferenceUse(JFieldVar field, ClassOutline classOutline) {
return getFieldReferenceUseAnnotationQName(field, classOutline) != null;
}
private boolean isContainer(JDefinedClass definedClass, Outline outline) {
ClassOutline classOutline = findClassOutline(definedClass, outline);
if (classOutline == null) {
return false;
}
boolean isContainer = hasAnnotation(classOutline, A_PRISM_CONTAINER)
|| hasAnnotation(classOutline, A_PRISM_OBJECT);
if (isContainer) {
return true;
}
if (!(definedClass._extends() instanceof JDefinedClass)) {
return false;
}
return isContainer((JDefinedClass) definedClass._extends(), outline);
}
private boolean isFieldTypeContainer(JFieldVar field, ClassOutline classOutline) {
JType type = field.type();
return isFieldTypeContainer(type, classOutline);
}
private boolean isFieldTypeContainer(JType type, ClassOutline classOutline) {
if (type instanceof JDefinedClass) {
return isContainer((JDefinedClass) type, classOutline.parent());
} else if (isList(type)) {
JClass clazz = (JClass) type;
return isFieldTypeContainer(clazz.getTypeParameters().get(0), classOutline);
}
return false;
}
private boolean updateSimpleField(JFieldVar field, ClassOutline classOutline, String baseMethod) {
//getter method update
JMethod method = recreateGetter(field, classOutline);
JBlock body = method.body();
body._return(JExpr.invoke(baseMethod).invoke(getGetterMethodName(classOutline, field)));
//setter method update
method = recreateSetter(field, classOutline);
body = method.body();
JInvocation invocation = body.invoke(JExpr.invoke(baseMethod), getSetterMethodName(classOutline, field));
invocation.arg(method.listParams()[0]);
return true;
}
private boolean updateContainerFieldType(JFieldVar field, ClassOutline classOutline) {
//getter method update
JMethod getterMethod = recreateGetter(field, classOutline);
annotateMethodWithXmlElement(getterMethod, field);
createContainerFieldGetterBody(field, classOutline, getterMethod);
//setter method update
if (!isList(field.type())) {
JMethod setterMethod = recreateSetter(field, classOutline);
createContainerFieldSetterBody(field, classOutline, setterMethod);
} else {
createFieldListCreator(field, classOutline, getterMethod, "createContainer");
}
return true;
}
private void createFluentFieldMethods(JFieldVar field, ClassOutline targetClass, ClassOutline fieldsFromClass) {
print("createFluentFieldMethods for " + field.name() + " on " + targetClass.implClass.fullName() + " (from " + fieldsFromClass.implClass.fullName() + ")");
JMethod fluentSetter = createFluentSetter(field, targetClass, fieldsFromClass);
createMethodStringVersion(field, targetClass, fluentSetter);
// FIXME ugly hack - we create beginXYZ only for our own structures
// TODO not for all!
JType basicType = getContentType(field);
if (basicType.fullName().startsWith("com.evolveum.") && isInstantiable(basicType)) {
createFluentBegin(field, targetClass, fieldsFromClass, fluentSetter);
}
}
/**
* e.g. name(PolyStringType value) -> name(String value) { return name(PolyStringType.fromOrig(value); }
* time(XmlGregorianCalendar value) -> time(String value) { return time(XmlTypeConverter.createXMLGregorianCalendar(value); }
* also ObjectReferenceType: create by oid + type + [relation]
*/
private void createMethodStringVersion(JFieldVar field, ClassOutline classOutline, JMethod originalMethod) {
Function<JVar,JExpression> expression;
JVar param = originalMethod.params().get(0);
if (param.type().fullName().equals(PolyStringType.class.getName())) {
expression = value -> JExpr.invoke(originalMethod)
.arg(CLASS_MAP.get(PolyStringType.class).staticInvoke("fromOrig").arg(value));
} else if (param.type().fullName().equals(XMLGregorianCalendar.class.getName())) {
expression = value -> JExpr.invoke(originalMethod)
.arg(CLASS_MAP.get(XmlTypeConverter.class).staticInvoke("createXMLGregorianCalendar").arg(value));
} else if (param.type().name().equals(ObjectReferenceType.class.getSimpleName())) {
createReferenceStringVersionOidType(field, classOutline, originalMethod, param.type());
createReferenceStringVersionOidTypeRelation(field, classOutline, originalMethod, param.type());
return;
} else {
return;
}
JMethod newMethod = classOutline.implClass.method(JMod.PUBLIC, originalMethod.type(), originalMethod.name());
JVar value = newMethod.param(String.class, "value");
newMethod.body()._return(expression.apply(value));
}
private void createReferenceStringVersionOidType(JFieldVar field, ClassOutline classOutline, JMethod originalMethod, JType objectReferenceType) {
JMethod newMethod = classOutline.implClass.method(JMod.PUBLIC, originalMethod.type(), originalMethod.name());
JVar oid = newMethod.param(String.class, "oid");
JVar type = newMethod.param(QName.class, "type");
JBlock body = newMethod.body();
JVar refVal = body.decl(CLASS_MAP.get(PrismReferenceValue.class), "refVal",
JExpr._new(CLASS_MAP.get(PrismReferenceValue.class))
.arg(oid).arg(type));
JVar ort = body.decl(objectReferenceType, "ort", JExpr._new(objectReferenceType));
body.invoke(ort, METHOD_SETUP_REFERENCE_VALUE).arg(refVal);
body._return(JExpr.invoke(originalMethod).arg(ort));
}
private void createReferenceStringVersionOidTypeRelation(JFieldVar field, ClassOutline classOutline, JMethod originalMethod, JType objectReferenceType) {
JMethod newMethod = classOutline.implClass.method(JMod.PUBLIC, originalMethod.type(), originalMethod.name());
JVar oid = newMethod.param(String.class, "oid");
JVar type = newMethod.param(QName.class, "type");
JVar relation = newMethod.param(QName.class, "relation");
JBlock body = newMethod.body();
JVar refVal = body.decl(CLASS_MAP.get(PrismReferenceValue.class), "refVal",
JExpr._new(CLASS_MAP.get(PrismReferenceValue.class))
.arg(oid).arg(type));
body.invoke(refVal, "setRelation").arg(relation);
JVar ort = body.decl(objectReferenceType, "ort", JExpr._new(objectReferenceType));
body.invoke(ort, METHOD_SETUP_REFERENCE_VALUE).arg(refVal);
body._return(JExpr.invoke(originalMethod).arg(ort));
}
private boolean isInstantiable(JType type) {
if (!(type instanceof JClass)) {
return false;
}
JClass clazz = (JClass) type;
if (clazz.isAbstract()) {
return false;
}
if (clazz instanceof JDefinedClass) {
if (hasAnnotationClass(((JDefinedClass) clazz), XmlEnum.class)) {
return false;
}
}
return true;
}
private void createContainerFieldSetterBody(JFieldVar field, ClassOutline classOutline, JMethod method) {
JVar param = method.listParams()[0];
JBlock body = method.body();
JVar cont;
if (isPrismContainer(param.type(), classOutline.parent())) {
cont = body.decl(CLASS_MAP.get(PrismObject.class), FIELD_CONTAINER_VALUE_LOCAL_VAR_NAME, JOp.cond(param.ne(JExpr._null()),
JExpr.invoke(param, METHOD_AS_PRISM_CONTAINER_VALUE), JExpr._null()));
} else {
cont = body.decl(CLASS_MAP.get(PrismContainerValue.class), FIELD_CONTAINER_VALUE_LOCAL_VAR_NAME,
JOp.cond(param.ne(JExpr._null()), JExpr.invoke(param, METHOD_AS_PRISM_CONTAINER_VALUE), JExpr._null()));
}
JInvocation invocation = body.staticInvoke(CLASS_MAP.get(PrismForJAXBUtil.class),
METHOD_PRISM_UTIL_SET_FIELD_CONTAINER_VALUE);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())));
invocation.arg(cont);
}
/*
public AssignmentType beginAssignment() {
AssignmentType value = new AssignmentType();
addAssignment(value);
return value;
}
*/
private JMethod createFluentBegin(JFieldVar field, ClassOutline targetClass, ClassOutline fieldsFromClass,
JMethod fluentSetter) {
JType valueClass = getContentType(field);
// e.g. public AssignmentType beginAssignment() {
String methodName = getMethodName(fieldsFromClass, field, "begin");
JMethod beginMethod = targetClass.implClass.method(JMod.PUBLIC, valueClass, methodName);
JBlock body = beginMethod.body();
// AssignmentType value = new AssignmentType();
JVar value = body.decl(valueClass, "value", JExpr._new(valueClass));
// addAssignment(value);
body.invoke(fluentSetter).arg(value);
// return value;
body._return(value);
return beginMethod;
}
/*
public <X> X endFocus() {
return (X) ((PrismContainerValue) ((PrismContainer) asPrismContainerValue().getParent()).getParent()).asContainerable();
}
*/
private JMethod createContainerFluentEnd(ClassOutline classOutline) {
String methodName = "end";
JMethod method = classOutline.implClass.method(JMod.PUBLIC, (JType) null, methodName);
method.type(method.generify("X"));
JBlock body = method.body();
body._return(JExpr.cast(method.type(),
JExpr.invoke(JExpr.cast(CLASS_MAP.get(PrismContainerValue.class),
JExpr.invoke(JExpr.cast(CLASS_MAP.get(PrismContainer.class),
JExpr.invoke(JExpr.invoke("asPrismContainerValue"),"getParent")), "getParent")),
"asContainerable")));
return method;
}
private JMethod createReferenceFluentEnd(ClassOutline classOutline) {
String methodName = "end";
JMethod method = classOutline.implClass.method(JMod.PUBLIC, (JType) null, methodName);
method.type(method.generify("X"));
JBlock body = method.body();
body._return(JExpr.cast(method.type(),
JExpr.invoke(JExpr.cast(CLASS_MAP.get(PrismContainerValue.class),
JExpr.invoke(JExpr.cast(CLASS_MAP.get(PrismReference.class),
JExpr.invoke(JExpr.invoke("asReferenceValue"),"getParent")), "getParent")),
"asContainerable")));
return method;
}
private JType getContentType(JFieldVar field) {
boolean multi = isList(field.type());
JType valueClass;
if (multi) {
valueClass = ((JClass) field.type()).getTypeParameters().get(0);
} else {
valueClass = field.type();
}
return valueClass;
}
private JMethod createFluentSetter(JFieldVar field, ClassOutline targetClass, ClassOutline fieldsFromClass) {
// e.g. UserType name(String value)
String methodName = getFluentSetterMethodName(fieldsFromClass, field);
JMethod fluentSetter = targetClass.implClass.method(JMod.PUBLIC, targetClass.implClass, methodName);
JType contentType = getContentType(field);
JVar value = fluentSetter.param(contentType, "value");
JBlock body = fluentSetter.body();
if (isList(field.type())) {
// getAssignment().add(value)
JMethod getterMethod = findGetterMethod(field, fieldsFromClass);
body.invoke(JExpr.invoke(getterMethod), "add").arg(value);
} else {
// setName(value);
JMethod setterMethod = findSetterMethod(field, fieldsFromClass);
body.invoke(setterMethod).arg(value);
}
// return this;
body._return(JExpr._this());
return fluentSetter;
}
private JMethod createFluentAdder(JFieldVar field, ClassOutline targetClass, ClassOutline fieldsFromClass) {
JType valueType = getContentType(field);
// e.g. FocusType assignment(AssignmentType value)
//String methodName = getMethodName(fieldsFromClass, field, "add");
String methodName = getFluentSetterMethodName(fieldsFromClass, field);
JMethod fluentSetter = targetClass.implClass.method(JMod.PUBLIC, targetClass.implClass, methodName);
JVar value = fluentSetter.param(valueType, "value");
JBlock body = fluentSetter.body();
// return this;
body._return(JExpr._this());
return fluentSetter;
}
private JMethod createFieldListCreator(JFieldVar field, ClassOutline classOutline, JMethod getterMethod, String itemCreationMethodName) {
JFieldRef qnameRef = JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())); // F_ASSIGNMENT
// e.g. List<AssignmentType> createAssignmentList()
String methodName = getMethodName(classOutline, field, "create") + "List";
JMethod method = classOutline.implClass.method(JMod.PUBLIC, field.type(), methodName);
JBlock body = method.body();
// PrismForJAXBUtil.createContainer(asPrismContainerValue(), F_ASSIGNMENT)
body.staticInvoke(CLASS_MAP.get(PrismForJAXBUtil.class), itemCreationMethodName)
.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE))
.arg(qnameRef);
// return getAssignment();
body._return(JExpr.invoke(getterMethod));
return method;
}
private JDefinedClass createFieldContainerGetterListAnon(JFieldVar field, ClassOutline classOutline) {
//add generics type to list field.type.getTypeParameters()...
JClass type = ((JClass) field.type()).getTypeParameters().get(0);
JClass clazz = CLASS_MAP.get(PrismContainerArrayList.class).narrow(type);
JDefinedClass anonymous = createAnonListClass(field, classOutline);
anonymous._implements(Serializable.class);
anonymous._extends(clazz);
JClass list = (JClass) field.type();
JClass listType = list.getTypeParameters().get(0);
JMethod constructor = anonymous.constructor(JMod.PUBLIC);
constructor.param(CLASS_MAP.get(PrismContainer.class), "container");
constructor.param(CLASS_MAP.get(PrismContainerValue.class), "parent");
JBlock constructorBody = constructor.body();
JInvocation invocation = constructorBody.invoke("super");
invocation.arg(constructor.listParams()[0]);
invocation.arg(constructor.listParams()[1]);
// Default constructor, for deserialization
JMethod defaultConstructor = anonymous.constructor(JMod.PUBLIC);
JBlock defaultConstructorBody = defaultConstructor.body();
defaultConstructorBody.invoke("super");
JMethod createItem = anonymous.method(JMod.PROTECTED, listType, "createItem");
createItem.annotate(CLASS_MAP.get(Override.class));
createItem.param(CLASS_MAP.get(PrismContainerValue.class), "value");
JMethod getValueFrom = anonymous.method(JMod.PROTECTED, CLASS_MAP.get(PrismContainerValue.class), "getValueFrom");
getValueFrom.annotate(CLASS_MAP.get(Override.class));
getValueFrom.param(listType, "value");
return anonymous;
}
private void createFieldContainerCreateItemBody(JFieldVar field, JMethod method) {
JClass list = (JClass) field.type();
JClass listType = list.getTypeParameters().get(0);
JBlock body = method.body();
JVar decl = body.decl(listType, field.name(), constructorExpression(method, listType));
JInvocation invocation = body.invoke(decl, METHOD_SETUP_CONTAINER_VALUE);
invocation.arg(method.listParams()[0]);
body._return(decl);
}
private void createFieldContainerGetValueFrom(JFieldVar field, JMethod method) {
JBlock body = method.body();
body._return(JExpr.invoke(method.listParams()[0], METHOD_AS_PRISM_CONTAINER_VALUE));
}
private void createContainerFieldGetterBody(JFieldVar field, ClassOutline classOutline, JMethod method) {
JBlock body = method.body();
List<JAnnotationUse> existingAnnotations = (List<JAnnotationUse>) getAnnotations(method);
for (JAnnotationUse annotation : existingAnnotations) {
if (isAnnotationTypeOf(annotation, XmlElement.class)) {
Field mfield = getField(JAnnotationUse.class, "memberValues");
mfield.setAccessible(true);
Map<String, Object> map;
try {
map = (Map<String, Object>) mfield.get(annotation);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
mfield.setAccessible(false);
map.remove("name");
annotation.param("name", normalizeFieldName(field.name()));
}
}
if (isList(field.type())) {
//JClass list = (JClass) field.type();
//JClass listType = list.getTypeParameters().get(0);
// PrismContainerValue pcv = asPrismContainerValue()
JVar pcvVar = body.decl(CLASS_MAP.get(PrismContainerValue.class), "pcv", JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
// PrismContainer container = PrismForJAXBUtil.getContainer(pcv, F_ASSIGNMENT);
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_CONTAINER);
invocation.arg(pcvVar);
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())));
JVar containerVar = body.decl(CLASS_MAP.get(PrismContainer.class), "container", invocation);
// anonymous class (e.g. FocusType.AnonAssignment and its methods)
JDefinedClass anonymousClass = createFieldContainerGetterListAnon(field, classOutline);
createFieldContainerCreateItemBody(field, findMethod(anonymousClass, "createItem"));
createFieldContainerGetValueFrom(field, findMethod(anonymousClass, "getValueFrom"));
// return new FocusType.AnonAssignment(container, pcv);
JInvocation newList = JExpr._new(anonymousClass);
newList.arg(containerVar);
newList.arg(pcvVar);
body._return(newList);
return;
}
JInvocation invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_FIELD_SINGLE_CONTAINERABLE);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())));
invocation.arg(JExpr.dotclass((JClass) field.type()));
body._return(invocation);
}
private boolean isPrismContainer(JType type, Outline outline) {
if (!(type instanceof JDefinedClass)) {
return false;
}
ClassOutline classOutline = findClassOutline((JDefinedClass) type, outline);
if (classOutline == null) {
return false;
}
return hasParentAnnotation(classOutline, A_PRISM_OBJECT);
}
private boolean isList(JType type) {
boolean isList = false;
if (type instanceof JClass) {
isList = CLASS_MAP.get(List.class).equals(((JClass) type).erasure());
}
return isList;
}
private void annotateFieldAsRaw(JFieldVar fieldVar) {
fieldVar.annotate(CLASS_MAP.get(Raw.class));
}
private void annotateMethodWithXmlElement(JMethod method, JFieldVar field) {
List<JAnnotationUse> existingAnnotations = getAnnotations(method);
for (JAnnotationUse annotation : existingAnnotations) {
if (isAnnotationTypeOf(annotation, XmlAttribute.class) ||
isAnnotationTypeOf(annotation, XmlAnyElement.class) ||
isAnnotationTypeOf(annotation, XmlAnyAttribute.class)) {
return;
}
}
JAnnotationUse xmlElement = null;
for (JAnnotationUse annotation : existingAnnotations) {
if (!isAnnotationTypeOf(annotation, XmlElement.class)) {
continue;
}
xmlElement = annotation;
break;
}
if (xmlElement == null) {
xmlElement = method.annotate(CLASS_MAP.get(XmlElement.class));
}
xmlElement.param("name", field.name());
}
private boolean updateField(JFieldVar field, ClassOutline classOutline) {
//update getter
JMethod getterMethod = recreateGetter(field, classOutline);
annotateMethodWithXmlElement(getterMethod, field);
boolean isList = isList(field.type());
createFieldGetterBody(getterMethod, field, isList);
//update setter
if (!isList) {
JMethod setterMethod = recreateSetter(field, classOutline);
createFieldSetterBody(setterMethod, field);
} else {
if (!hasAnnotationClass(field, XmlAnyElement.class)) {
createFieldListCreator(field, classOutline, getterMethod, "createProperty");
}
}
return true;
}
private void createFieldSetterBody(JMethod method, JFieldVar field) {
JBlock body = method.body();
JInvocation invocation = body.staticInvoke(CLASS_MAP.get(PrismForJAXBUtil.class),
METHOD_PRISM_UTIL_SET_PROPERTY_VALUE);
//push arguments
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())));
invocation.arg(method.listParams()[0]);
}
private <T> boolean hasAnnotationClass(JAnnotatable method, Class<T> annotationType) {
List<JAnnotationUse> annotations = getAnnotations(method);
for (JAnnotationUse annotation : annotations) {
if (isAnnotationTypeOf(annotation, annotationType)) {
return true;
}
}
return false;
}
private void createFieldGetterBody(JMethod method, JFieldVar field, boolean isList) {
JBlock body = method.body();
JInvocation invocation;
if (hasAnnotationClass(method, XmlAnyElement.class)) {
//handling xsd any
invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_GET_ANY);
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
JClass clazz = (JClass) field.type();
invocation.arg(JExpr.dotclass(clazz.getTypeParameters().get(0)));
body._return(invocation);
return;
}
if (isList) {
invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_PROPERTY_VALUES);
} else {
invocation = CLASS_MAP.get(PrismForJAXBUtil.class).staticInvoke(METHOD_PRISM_UTIL_GET_PROPERTY_VALUE);
}
//push arguments
invocation.arg(JExpr.invoke(METHOD_AS_PRISM_CONTAINER_VALUE));
invocation.arg(JExpr.ref(fieldFPrefixUnderscoredUpperCase(field.name())));
JType type = field.type();
if (type.isPrimitive()) {
JPrimitiveType primitive = (JPrimitiveType) type;
invocation.arg(JExpr.dotclass(primitive.boxify()));
} else {
JClass clazz = (JClass) type;
if (isList) {
invocation.arg(JExpr.dotclass(clazz.getTypeParameters().get(0)));
} else {
invocation.arg(JExpr.dotclass(clazz));
}
}
body._return(invocation);
}
public static void print(String s) {
if (PRINT_DEBUG_INFO) {
System.out.println(s);
}
}
public static void printWarning(String s) {
System.out.println(s);
}
}