/*
* Copyright (c) 2010-2015 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.prism.xjc;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.xnode.MapXNode;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.apache.commons.lang.Validate;
import org.w3c.dom.Element;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.namespace.QName;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author lazyman
*/
public final class PrismForJAXBUtil {
private PrismForJAXBUtil() {
}
public static <T> T getPropertyValue(PrismContainerValue container, QName name, Class<T> clazz) {
Validate.notNull(container, "Container must not be null.");
Validate.notNull(name, "QName must not be null.");
Validate.notNull(clazz, "Class type must not be null.");
PrismProperty property = container.findProperty(name);
return getPropertyValue(property, clazz);
}
private static <T> T getPropertyValue(PrismProperty<?> property, Class<T> requestedType) {
if (property == null) {
return null;
}
PrismPropertyValue<?> pvalue = property.getValue();
if (pvalue == null) {
return null;
}
Object propertyRealValue = pvalue.getValue();
if (propertyRealValue instanceof Element) {
if (requestedType.isAssignableFrom(Element.class)) {
return (T) propertyRealValue;
}
Field anyField = getAnyField(requestedType);
if (anyField == null) {
throw new IllegalArgumentException("Attempt to read raw property "+property+" while the requested class ("+requestedType+") does not have 'any' field");
}
anyField.setAccessible(true);
Collection<?> anyElementList = property.getRealValues();
T requestedTypeInstance;
try {
requestedTypeInstance = requestedType.newInstance();
anyField.set(requestedTypeInstance, anyElementList);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Instantiate error while reading raw property "+property+", requested class ("+requestedType+"):"
+e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Illegal access error while reading raw property "+property+", requested class ("+requestedType+")"
+", field "+anyField+": "+e.getMessage(), e);
}
return requestedTypeInstance;
}
return JaxbTypeConverter.mapPropertyRealValueToJaxb(propertyRealValue);
}
private static <T> Field getAnyField(Class<T> clazz) {
for (Field field: clazz.getDeclaredFields()) {
XmlAnyElement xmlAnyElementAnnotation = field.getAnnotation(XmlAnyElement.class);
if (xmlAnyElementAnnotation != null) {
return field;
}
}
return null;
}
public static <T> List<T> getPropertyValues(PrismContainerValue<?> container, QName name, Class<T> clazz) {
Validate.notNull(container, "Container must not be null.");
Validate.notNull(name, "QName must not be null.");
Validate.notNull(clazz, "Class type must not be null.");
PrismProperty<?> property;
try {
property = container.findProperty(name);
if (property == null) {
property = container.createDetachedSubItem(name, PrismProperty.class, null, container.isImmutable());
}
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
return new PropertyArrayList<>(property, container);
}
public static <T> void setPropertyValue(PrismContainerValue<?> container, QName name, T value) {
Validate.notNull(container, "Container must not be null.");
Validate.notNull(name, "QName must not be null.");
if (value == null) {
container.removeProperty(name);
} else {
PrismProperty<?> property;
try {
property = container.findOrCreateProperty(name);
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
Object propertyRealValue = JaxbTypeConverter.mapJaxbToPropertyRealValue(value);
if (propertyRealValue == null) {
container.removeProperty(name);
} else {
property.setValue(new PrismPropertyValue(propertyRealValue));
}
}
}
public static <T extends Containerable> PrismContainerValue<T> getFieldContainerValue(PrismContainerValue<?> parent, QName fieldName) {
Validate.notNull(parent, "Container value must not be null.");
Validate.notNull(fieldName, "Field QName must not be null.");
PrismContainer<T> container = parent.findItem(fieldName, PrismContainer.class);
return container != null ? container.getValue() : null;
}
public static <T extends Containerable> T getFieldSingleContainerable(PrismContainerValue<?> parent, QName fieldName, Class<T> fieldClass) {
PrismContainerValue<T> fieldContainerValue = getFieldContainerValue(parent, fieldName);
if (fieldContainerValue == null) {
return null;
}
return fieldContainerValue.asContainerable(fieldClass);
}
public static <T extends PrismContainer<?>> T getContainer(PrismContainerValue parentValue, QName name) {
Validate.notNull(parentValue, "Parent container value must not be null.");
Validate.notNull(name, "QName must not be null.");
try {
PrismContainer container = parentValue.findContainer(name);
if (container != null) {
return (T) container;
} else {
return (T) parentValue.createDetachedSubItem(name, PrismContainer.class, null, parentValue.isImmutable());
}
} catch (SchemaException ex) {
throw new SystemException(ex.getMessage(), ex);
}
}
public static PrismContainer<?> createContainer(PrismContainerValue parentValue, QName name) {
return createItem(parentValue, name, PrismContainer.class);
}
public static PrismReference createReference(PrismContainerValue parentValue, QName name) {
return createItem(parentValue, name, PrismReference.class);
}
public static PrismProperty<?> createProperty(PrismContainerValue parentValue, QName name) {
return createItem(parentValue, name, PrismProperty.class);
}
public static <IV extends PrismValue,ID extends ItemDefinition,I extends Item<IV,ID>> I createItem(PrismContainerValue parentValue, QName name, Class<I> type) {
Validate.notNull(parentValue, "Parent container value must not be null.");
Validate.notNull(name, "QName must not be null.");
try {
return (I) parentValue.findOrCreateItem(name, type);
} catch (SchemaException ex) {
throw new SystemException(ex.getMessage(), ex);
}
}
public static <T extends Containerable> boolean setFieldContainerValue(PrismContainerValue<?> parent, QName fieldName,
PrismContainerValue<T> fieldContainerValue) {
Validate.notNull(parent, "Prism container value must not be null.");
Validate.notNull(fieldName, "QName must not be null.");
try {
PrismContainer<T> fieldContainer = null;
if (fieldContainerValue == null) {
parent.removeContainer(fieldName);
} else {
if (fieldContainerValue.getParent() != null && fieldContainerValue.getParent() != parent) {
// This value is already part of another prism. We need to clone it to add it here.
fieldContainerValue = fieldContainerValue.clone();
}
fieldContainer = new PrismContainer<T>(fieldName, parent.getPrismContext());
fieldContainer.add(fieldContainerValue);
if (parent.getParent() == null) {
parent.add(fieldContainer);
} else {
parent.addReplaceExisting(fieldContainer);
}
}
// // Make sure that the definition from parent is applied to new field container
// if (fieldContainer.getDefinition() == null) {
// PrismContainer<?> parentContainer = parent.getContainer();
// if (parentContainer != null) {
// PrismContainerDefinition<?> parentDefinition = parentContainer.getDefinition();
// if (parentDefinition != null) {
// PrismContainerDefinition<T> fieldDefinition = parentDefinition.findContainerDefinition(fieldName);
// fieldContainer.setDefinition(fieldDefinition);
// }
// }
// }
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
return true;
}
public static PrismReferenceValue getReferenceValue(PrismContainerValue<?> parent, QName name) {
Validate.notNull(parent, "Prism container value must not be null.");
Validate.notNull(name, "QName must not be null.");
PrismReference reference = parent.findItem(name, PrismReference.class);
return reference != null ? reference.getValue() : null;
}
/**
* This method must merge new value with potential existing value of the reference.
* E.g. it is possible to call setResource(..) and then setResourceRef(..) with the
* same OID. In that case the result should be one reference that has both OID/type/filter
* and object.
* Assumes single-value reference
*/
public static void setReferenceValueAsRef(PrismContainerValue<?> parentValue, QName referenceName,
PrismReferenceValue value) {
Validate.notNull(parentValue, "Prism container value must not be null.");
Validate.notNull(referenceName, "QName must not be null.");
PrismReference reference;
try {
reference = parentValue.findOrCreateItem(referenceName, PrismReference.class);
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
if (reference == null) {
throw new IllegalArgumentException("No reference "+referenceName+" in "+parentValue);
}
if (value == null) {
parentValue.remove(reference);
} else {
if (reference.isEmpty()) {
if (value.getParent() != null) {
value = value.clone();
}
reference.add(value);
} else {
reference.getValue().setOid(value.getOid());
reference.getValue().setTargetType(value.getTargetType());
reference.getValue().setFilter(value.getFilter());
reference.getValue().setDescription(value.getDescription());
}
}
}
/**
* This method must merge new value with potential existing value of the reference.
* E.g. it is possible to call setResource(..) and then setResourceRef(..) with the
* same OID. In that case the result should be one reference that has both OID/type/filter
* and object.
* Assumes single-value reference
*/
public static void setReferenceValueAsObject(PrismContainerValue parentValue, QName referenceQName, PrismObject targetObject) {
Validate.notNull(parentValue, "Prism container value must not be null.");
Validate.notNull(referenceQName, "QName must not be null.");
PrismReference reference;
try {
reference = parentValue.findOrCreateReference(referenceQName);
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
if (reference == null) {
throw new IllegalArgumentException("No reference "+referenceQName+" in "+parentValue);
}
PrismReferenceValue referenceValue = reference.getValue();
referenceValue.setObject(targetObject);
}
public static <T extends Objectable> PrismReferenceValue objectableAsReferenceValue(T objectable, PrismReference reference ) {
PrismObject<T> object = objectable.asPrismObject();
for (PrismReferenceValue refValue: reference.getValues()) {
if (object == refValue.getObject()) {
return refValue;
}
}
PrismReferenceValue referenceValue = new PrismReferenceValue();
referenceValue.setObject(object);
return referenceValue;
}
public static <T> List<T> getAny(PrismContainerValue value, Class<T> clazz) {
return new AnyArrayList(value);
}
public static PrismObject setupContainerValue(PrismObject prismObject, PrismContainerValue containerValue) {
PrismContainerable parent = containerValue.getParent();
if (parent != null && parent instanceof PrismObject) {
return (PrismObject)parent;
}
try {
prismObject.setValue(containerValue);
return prismObject;
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
}
public static PrismReference getReference(PrismContainerValue parent, QName fieldName) {
try {
PrismReference reference = parent.findReference(fieldName);
if (reference != null) {
return reference;
} else {
return (PrismReference) parent.createDetachedSubItem(fieldName, PrismReference.class, null, parent.isImmutable());
}
} catch (SchemaException e) {
// This should not happen. Code generator and compiler should take care of that.
throw new IllegalStateException("Internal schema error: "+e.getMessage(),e);
}
}
public static void setReferenceFilterClauseXNode(PrismReferenceValue rval, SearchFilterType filterType) {
if (filterType != null) {
rval.setFilter(filterType.clone());
} else {
rval.setFilter(null);
}
}
public static MapXNode getReferenceFilterClauseXNode(PrismReferenceValue rval) {
SearchFilterType filter = rval.getFilter();
if (filter == null || !filter.containsFilterClause()) {
return null;
}
return filter.getFilterClauseXNode();
}
public static PolyStringType getReferenceTargetName(PrismReferenceValue rval) {
PolyString targetName = rval.getTargetName();
if (targetName == null) {
return null;
}
return new PolyStringType(targetName);
}
public static void setReferenceTargetName(PrismReferenceValue rval, PolyStringType name) {
if (name == null) {
rval.setTargetName((PolyString) null);
} else {
rval.setTargetName(name.toPolyString());
}
}
}