/*
* 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.util;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.marshaller.XPathHolder;
import com.evolveum.midpoint.prism.marshaller.XPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.RefFilter;
import com.evolveum.midpoint.prism.util.ItemPathUtil;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.apache.commons.collections4.CollectionUtils.emptyIfNull;
/**
* Methods that would belong to the ObjectType class but cannot go there because
* of JAXB.
* <p/>
* There are also useful methods that would belong to other classes. But we
* don't want to create new class for every method ... if this goes beyond a
* reasonable degree, please refactor accordingly.
*
* @author Radovan Semancik
*/
public class ObjectTypeUtil {
/**
* Never returns null. Returns empty collection instead.
*/
public static <T> Collection<T> getExtensionPropertyValuesNotNull(ObjectType objectType, QName propertyQname) {
Collection<T> values = getExtensionPropertyValues(objectType, propertyQname);
if (values == null) {
return new ArrayList<T>(0);
} else {
return values;
}
}
public static <T> Collection<T> getExtensionPropertyValues(ObjectType objectType, QName propertyQname) {
PrismObject<? extends ObjectType> object = objectType.asPrismObject();
PrismContainer<Containerable> extensionContainer = object.findContainer(ObjectType.F_EXTENSION);
if (extensionContainer == null) {
return null;
}
PrismProperty<T> property = extensionContainer.findProperty(propertyQname);
if (property == null) {
return null;
}
return property.getRealValues();
}
public static Collection<Referencable> getExtensionReferenceValues(ObjectType objectType, QName propertyQname) {
PrismObject<? extends ObjectType> object = objectType.asPrismObject();
PrismContainer<Containerable> extensionContainer = object.findContainer(ObjectType.F_EXTENSION);
if (extensionContainer == null) {
return null;
}
PrismReference property = extensionContainer.findReference(propertyQname);
if (property == null) {
return null;
}
Collection<Referencable> refs = new ArrayList<Referencable>(property.getValues().size());
for (PrismReferenceValue refVal : property.getValues()){
refs.add(refVal.asReferencable());
}
return refs;
}
public static ObjectReferenceType findRef(String oid, List<ObjectReferenceType> refs) {
for (ObjectReferenceType ref : refs) {
if (ref.getOid().equals(oid)) {
return ref;
}
}
return null;
}
public static String toShortString(PrismObject<? extends ObjectType> object) {
return toShortString(object != null ? object.asObjectable() : null);
}
public static String toShortString(ObjectType object) {
if (object == null) {
return "null";
}
StringBuilder builder = new StringBuilder();
builder.append(getShortTypeName(object));
builder.append(": ");
builder.append(object.getName());
builder.append(" (OID:");
builder.append(object.getOid());
builder.append(")");
return builder.toString();
}
public static String toShortString(AssignmentType assignment) {
if (assignment == null) {
return "null";
}
StringBuilder sb = new StringBuilder("Assignment(");
if (assignment.getConstruction() != null) {
sb.append("construction");
// TODO
}
if (assignment.getTarget() != null) {
sb.append(toShortString(assignment.getTarget()));
}
if (assignment.getTargetRef() != null) {
sb.append(toShortString(assignment.getTargetRef()));
}
sb.append(")");
return sb.toString();
}
public static String dump(ObjectType object) {
if (object == null) {
return "null";
}
return object.asPrismObject().debugDump();
}
public static Object toShortString(ObjectReferenceType objectRef) {
return toShortString(objectRef, false);
}
public static Object toShortString(ObjectReferenceType objectRef, boolean withName) {
if (objectRef == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
sb.append("objectRef oid=").append(objectRef.getOid());
if (withName && objectRef.getTargetName() != null) {
sb.append(" name='").append(objectRef.getTargetName()).append("'");
}
if (objectRef.getType() != null) {
sb.append(" type=").append(SchemaDebugUtil.prettyPrint(objectRef.getType()));
}
return sb.toString();
}
public static String getShortTypeName(ObjectType object) {
return getShortTypeName(object.getClass());
}
public static String getShortTypeName(Class<? extends ObjectType> type) {
ObjectTypes objectTypeType = ObjectTypes.getObjectType(type);
if (objectTypeType != null) {
return objectTypeType.getQName().getLocalPart();
} else {
return type.getSimpleName();
}
}
@NotNull
public static <T extends ObjectType> AssignmentType createAssignmentTo(@NotNull ObjectReferenceType ref, @Nullable PrismContext prismContext) {
AssignmentType assignment = new AssignmentType(prismContext);
if (QNameUtil.match(ref.getType(), ResourceType.COMPLEX_TYPE)) {
ConstructionType construction = new ConstructionType();
construction.setResourceRef(ref);
assignment.setConstruction(construction);
} else {
assignment.setTargetRef(ref);
}
return assignment;
}
@NotNull
public static <T extends ObjectType> AssignmentType createAssignmentTo(@NotNull PrismReferenceValue ref, @Nullable PrismContext prismContext) {
ObjectReferenceType ort = new ObjectReferenceType();
ort.setupReferenceValue(ref);
return createAssignmentTo(ort, prismContext);
}
@NotNull
public static <T extends ObjectType> AssignmentType createAssignmentTo(@NotNull String oid, @NotNull ObjectTypes type, @Nullable PrismContext prismContext) {
return createAssignmentTo(createObjectRef(oid, type), prismContext);
}
@NotNull
public static <T extends ObjectType> AssignmentType createAssignmentTo(@NotNull PrismObject<T> object) {
AssignmentType assignment = new AssignmentType(object.getPrismContext());
if (object.asObjectable() instanceof ResourceType) {
ConstructionType construction = new ConstructionType(object.getPrismContext());
construction.setResourceRef(createObjectRef(object));
assignment.setConstruction(construction);
} else {
assignment.setTargetRef(createObjectRef(object));
}
return assignment;
}
public static ObjectReferenceType createObjectRef(PrismReferenceValue prv) {
ObjectReferenceType ort = new ObjectReferenceType();
ort.setupReferenceValue(prv);
return ort;
}
public static ObjectReferenceType createObjectRef(ObjectType objectType) {
if (objectType == null) {
return null;
}
return createObjectRef(objectType.asPrismObject());
}
public static <T extends ObjectType> ObjectReferenceType createObjectRef(PrismObject<T> object) {
if (object == null) {
return null;
}
ObjectReferenceType ref = new ObjectReferenceType();
ref.setOid(object.getOid());
PrismObjectDefinition<T> definition = object.getDefinition();
if (definition != null) {
ref.setType(definition.getTypeName());
}
ref.setTargetName(object.asObjectable().getName());
return ref;
}
//FIXME TODO temporary hack
public static <T extends ObjectType> ObjectReferenceType createObjectRef(PrismObject<T> object, boolean nameAsDescription) {
if (object == null) {
return null;
}
ObjectReferenceType ref = new ObjectReferenceType();
ref.setOid(object.getOid());
if (nameAsDescription){
ref.setDescription(object.getBusinessDisplayName());
}
PrismObjectDefinition<T> definition = object.getDefinition();
if (definition != null) {
ref.setType(definition.getTypeName());
}
return ref;
}
public static <T extends ObjectType> ObjectReferenceType createObjectRef(PrismReferenceValue refVal, boolean nameAsDescription) {
if (refVal == null) {
return null;
}
ObjectReferenceType ref = new ObjectReferenceType();
ref.setOid(refVal.getOid());
PrismObject<T> object = refVal.getObject();
if (object != null) {
if (nameAsDescription) {
ref.setDescription(object.getBusinessDisplayName());
}
PrismObjectDefinition<T> definition = object.getDefinition();
if (definition != null) {
ref.setType(definition.getTypeName());
}
ref.setTargetName(PolyString.toPolyStringType(object.getName()));
} else {
ref.setType(refVal.getTargetType());
ref.setTargetName(PolyString.toPolyStringType(refVal.getTargetName()));
if (nameAsDescription && refVal.getTargetName() != null) {
ref.setDescription(refVal.getTargetName().getOrig());
}
}
return ref;
}
public static ObjectReferenceType createObjectRef(String oid, ObjectTypes type) {
return createObjectRef(oid, null, type);
}
public static ObjectReferenceType createObjectRef(String oid, PolyStringType name, ObjectTypes type) {
Validate.notEmpty(oid, "Oid must not be null or empty.");
Validate.notNull(type, "Object type must not be null.");
ObjectReferenceType reference = new ObjectReferenceType();
reference.setType(type.getTypeQName());
reference.setOid(oid);
reference.setTargetName(name);
return reference;
}
/**
* Returns the <xsd:schema> element from the XmlSchemaType.
*/
public static Element findXsdElement(XmlSchemaType xmlSchemaType) {
if (xmlSchemaType == null) {
return null;
}
PrismContainerValue<XmlSchemaType> xmlSchemaContainerValue = xmlSchemaType.asPrismContainerValue();
return findXsdElement(xmlSchemaContainerValue);
}
public static Element findXsdElement(PrismContainer<XmlSchemaType> xmlSchemaContainer) {
return findXsdElement(xmlSchemaContainer.getValue());
}
public static Element findXsdElement(PrismContainerValue<XmlSchemaType> xmlSchemaContainerValue) {
PrismProperty<SchemaDefinitionType> definitionProperty = xmlSchemaContainerValue.findProperty(XmlSchemaType.F_DEFINITION);
if (definitionProperty == null) {
return null;
}
SchemaDefinitionType schemaDefinition = definitionProperty.getValue().getValue();
if (schemaDefinition == null) {
return null;
}
return schemaDefinition.getSchema();
// List<Element> schemaElements = DOMUtil.listChildElements(definitionElement);
// for (Element e : schemaElements) {
// if (QNameUtil.compareQName(DOMUtil.XSD_SCHEMA_ELEMENT, e)) {
// DOMUtil.fixNamespaceDeclarations(e);
// return e;
// }
// }
// return null;
}
public static void setXsdSchemaDefinition(PrismProperty<SchemaDefinitionType> definitionProperty, Element xsdElement) {
// Document document = xsdElement.getOwnerDocument();
// Element definitionElement = document.createElementNS(XmlSchemaType.F_DEFINITION.getNamespaceURI(),
// XmlSchemaType.F_DEFINITION.getLocalPart());
// definitionElement.appendChild(xsdElement);
// SchemaDefinitionType schemaDefinition = definitionProperty.getValue().getValue();
// schemaDefinition.setSchema(definitionElement);
SchemaDefinitionType schemaDefinition = new SchemaDefinitionType();
schemaDefinition.setSchema(xsdElement);
definitionProperty.setRealValue(schemaDefinition);
}
public static XPathHolder createXPathHolder(QName property) {
XPathSegment xpathSegment = new XPathSegment(property);
List<XPathSegment> segmentlist = new ArrayList<XPathSegment>(1);
segmentlist.add(xpathSegment);
XPathHolder xpath = new XPathHolder(segmentlist);
return xpath;
}
public static boolean isModificationOf(ItemDeltaType modification, QName elementName) {
return isModificationOf(modification, elementName, null);
}
//TODO: refactor after new schema
public static boolean isModificationOf(ItemDeltaType modification, QName elementName, ItemPathType path) {
// if (path == null && XPathHolder.isDefault(modification.getPath())) {
// return (elementName.equals(ObjectTypeUtil.getElementName(modification)));
// }
ItemPathType modificationPath = modification.getPath();
if (ItemPathUtil.isDefault(modificationPath)){
throw new IllegalArgumentException("Path in the delta must not be null");
}
// if (path == null && ItemPathUtil.isDefault(modificationPath)) {
// return (elementName.equals(getElementName(modification)));
// }
if (path == null) {
return false;
}
// XPathHolder modPath = new XPathHolder(modification.getPath());
ItemPath full = new ItemPath(path.getItemPath(), elementName);
ItemPathType fullPath = new ItemPathType(full);
return fullPath.equivalent(modificationPath);
// if (fullPath.equals(modificationPath)) {
// return (elementName.equals(getElementName(modification)));
// }
// return false;
}
// public static QName getElementName(ItemDeltaType propertyModification) {
// if (propertyModification.getValue() == null) {
// throw new IllegalArgumentException("Modification without value element");
// }
// if (propertyModification.getValue().getContent() == null || propertyModification.getValue().getContent().isEmpty()) {
// throw new IllegalArgumentException("Modification with empty value element");
// }
// return JAXBUtil.getElementQName(propertyModification.getValue().getContent().get(0));
// }
// public static boolean isEmpty(ObjectModificationType objectModification) {
// return (objectModification.getItemDelta() == null) ||
// objectModification.getItemDelta().isEmpty();
// }
public static void assertConcreteType(Class<? extends Objectable> type) {
// The abstract object types are enumerated here. It should be switched to some flag later on
if (type.equals(ObjectType.class)) {
throw new IllegalArgumentException("The type "+type.getName()+" is abstract");
}
}
public static PrismObject getParentObject(Containerable containerable) {
if (containerable == null) {
return null;
}
PrismContainerable<? extends Containerable> parent1 = containerable.asPrismContainerValue().getParent();
if (parent1 == null) {
return null;
}
if (!(parent1 instanceof PrismContainer)) {
throw new IllegalArgumentException("Parent of " + containerable + " is not a PrismContainer. It is " + parent1.getClass());
}
PrismValue parent2 = ((PrismContainer) parent1).getParent();
if (parent2 == null) {
return null;
}
if (!(parent2 instanceof PrismContainerValue)) {
throw new IllegalArgumentException("Grandparent of " + containerable + " is not a PrismContainerValue. It is " + parent2.getClass());
}
Itemable parent3 = parent2.getParent();
if (parent3 == null) {
return null;
}
if (!(parent3 instanceof PrismObject)) {
throw new IllegalArgumentException("Grandgrandparent of " + containerable + " is not a PrismObject. It is " + parent3.getClass());
}
return (PrismObject) parent3;
}
public static List<PrismReferenceValue> objectReferenceListToPrismReferenceValues(Collection<ObjectReferenceType> refList) throws SchemaException {
List<PrismReferenceValue> rv = new ArrayList<>();
for (ObjectReferenceType ref : refList) {
rv.add(ref.asReferenceValue());
}
return rv;
}
public static List<ObjectReferenceType> getAsObjectReferenceTypeList(PrismReference prismReference) throws SchemaException {
List<ObjectReferenceType> rv = new ArrayList<>();
for (PrismReferenceValue prv : prismReference.getValues()) {
rv.add(createObjectRef(prv.clone()));
}
return rv;
}
public static List<String> referenceValueListToOidList(Collection<PrismReferenceValue> referenceValues) {
List<String> oids = new ArrayList<>(referenceValues.size());
for (PrismReferenceValue referenceValue : referenceValues) {
oids.add(referenceValue.getOid());
}
return oids;
}
public static Objectable getObjectFromReference(ObjectReferenceType ref) {
if (ref == null) {
return null;
}
if (ref.asReferenceValue().getObject() == null) {
return null;
}
return ref.asReferenceValue().getObject().asObjectable();
}
public static PrismObject<?> getPrismObjectFromReference(ObjectReferenceType ref) {
if (ref == null) {
return null;
}
return ref.asReferenceValue().getObject();
}
public static List<ObjectDelta<? extends ObjectType>> toDeltaList(ObjectDelta<?> delta) {
@SuppressWarnings("unchecked")
ObjectDelta<? extends ObjectType> objectDelta = (ObjectDelta<? extends ObjectType>) delta;
return Collections.<ObjectDelta<? extends ObjectType>>singletonList(objectDelta);
}
// Hack: because DeltaBuilder cannot provide ObjectDelta<? extends ObjectType> (it is from schema)
public static Collection<ObjectDelta<? extends ObjectType>> cast(Collection<ObjectDelta<?>> deltas) {
@SuppressWarnings("unchecked")
final Collection<ObjectDelta<? extends ObjectType>> deltas1 = (Collection) deltas;
return deltas1;
}
public static PolyStringType getDisplayName(PrismObject<?> object) {
return object != null ? getDisplayName((ObjectType) object.asObjectable()) : null;
}
public static PolyStringType getDisplayName(ObjectType object) {
if (object instanceof AbstractRoleType) {
return ((AbstractRoleType) object).getDisplayName();
} else if (object instanceof UserType) {
return ((UserType) object).getFullName();
} else {
return null;
}
}
public static PolyStringType getDisplayName(ObjectReferenceType ref) {
return ref != null ? getDisplayName(ref.asReferenceValue().getObject()) : null;
}
public static ObjectType toObjectable(PrismObject object) {
return object != null ? (ObjectType) object.asObjectable() : null;
}
public static boolean containsOid(Collection<ObjectReferenceType> values, @NotNull String oid) {
return values.stream().anyMatch(v -> oid.equals(v.getOid()));
}
@SuppressWarnings("unchecked")
public static <T> T getExtensionItemRealValue(@Nullable ExtensionType extension, @NotNull QName itemName) {
if (extension == null) {
return null;
}
Item item = extension.asPrismContainerValue().findItem(itemName);
return item != null ? (T) item.getRealValue() : null;
}
public static QName normalizeRelation(QName name) {
if (name == null) {
return SchemaConstants.ORG_DEFAULT;
} else {
return QNameUtil.setNamespaceIfMissing(name, SchemaConstants.NS_ORG, SchemaConstants.PREFIX_NS_ORG);
}
}
public static void normalizeRelation(ObjectReferenceType reference) {
if (reference != null) {
reference.setRelation(normalizeRelation(reference.getRelation()));
}
}
public static void normalizeRelation(PrismReferenceValue reference) {
if (reference != null) {
reference.setRelation(normalizeRelation(reference.getRelation()));
}
}
public static void normalizeAllRelations(PrismValue value) {
if (value != null) {
value.accept(createNormalizingVisitor());
}
}
public static void normalizeAllRelations(Item<?,?> item) {
if (item != null) {
item.accept(createNormalizingVisitor());
}
}
private static Visitor createNormalizingVisitor() {
return v -> {
if (v instanceof PrismReferenceValue) {
normalizeRelation((PrismReferenceValue) v);
}
};
}
public static void normalizeFilter(ObjectFilter filter) {
if (filter != null) {
filter.accept(f -> {
if (f instanceof RefFilter) {
emptyIfNull(((RefFilter) f).getValues()).forEach(v -> normalizeRelation(v));
}
});
}
}
// This is not the right place for this. But let's leave it here for now.
// See MID-3581
public static boolean isDelegationRelation(QName relation) {
return QNameUtil.match(relation, SchemaConstants.ORG_DEPUTY);
}
// This is not the right place for this. But let's leave it here for now.
// See MID-3581
public static boolean isMembershipRelation(QName relation) {
return isDefaultRelation(relation)
|| isManagerRelation(relation)
|| QNameUtil.match(relation, SchemaConstants.ORG_META);
}
// This is not the right place for this. But let's leave it here for now.
// See MID-3581
public static boolean isManagerRelation(QName relation) {
return QNameUtil.match(relation, SchemaConstants.ORG_MANAGER);
}
// This is not the right place for this. But let's leave it here for now.
// See MID-3581
public static boolean isDefaultRelation(QName relation) {
return relation == null || QNameUtil.match(relation, SchemaConstants.ORG_DEFAULT);
}
public static boolean relationMatches(QName relationQuery, QName relation) {
return QNameUtil.match(relationQuery, PrismConstants.Q_ANY) || relationsEquivalent(relationQuery, relation);
}
public static boolean relationsEquivalent(QName relation1, QName relation2) {
if (ObjectTypeUtil.isDefaultRelation(relation1)) {
return ObjectTypeUtil.isDefaultRelation(relation2);
} else {
return QNameUtil.match(relation1, relation2);
}
}
public static OrderConstraintsType getConstraintFor(List<OrderConstraintsType> constraints, QName relation) {
return CollectionUtils.emptyIfNull(constraints).stream()
.filter(c -> QNameUtil.match(c.getRelation(), relation)) // intentionally not using default/null equivalence here
.findFirst().orElse(null);
}
public static <T extends Objectable> T asObjectable(PrismObject<T> prismObject) {
return prismObject != null ? prismObject.asObjectable() : null;
}
}