/*
* 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.prism.marshaller;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.schema.PrismSchema;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.prism.util.PrismUtil;
import com.evolveum.midpoint.prism.xnode.*;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_3.EvaluationTimeType;
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 javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import java.util.Map.Entry;
public class PrismUnmarshaller {
private static final Trace LOGGER = TraceManager.getTrace(PrismUnmarshaller.class);
private static final QName ARTIFICIAL_OBJECT_NAME = new QName(XMLConstants.NULL_NS_URI, "anObject");
@NotNull
private PrismContext prismContext;
public PrismUnmarshaller(@NotNull PrismContext prismContext) {
this.prismContext = prismContext;
}
//region Public interface ========================================================
/*
* Please note: methods in this section should NOT be called from inside of parsing process!
* It is to avoid repeatedly calling ItemInfo.determine, if at all possible.
* (An exception is only if we know we have the definition ... TODO ...)
*
* TODO migrate to parseItem eventually (now we treat objects in parseItemInternal!)
*/
@SuppressWarnings("unchecked")
<O extends Objectable> PrismObject<O> parseObject(@NotNull RootXNode root, ItemDefinition<?> itemDefinition, QName itemName,
QName typeName, Class<?> typeClass, @NotNull ParsingContext pc) throws SchemaException {
ItemInfo itemInfo = ItemInfo.determine(itemDefinition,
root.getRootElementName(), itemName, ARTIFICIAL_OBJECT_NAME,
root.getTypeQName(), typeName,
typeClass, PrismObjectDefinition.class, pc, getSchemaRegistry());
XNode child = root.getSubnode();
if (!(child instanceof MapXNode)) {
throw new IllegalArgumentException("Cannot parse object from " + child.getClass().getSimpleName() + ", we need a map");
}
return (PrismObject<O>) (Item) parseItemInternal(child, itemInfo.getItemName(), itemInfo.getItemDefinition(), pc);
}
// TODO migrate to parseItem eventually
@SuppressWarnings("unchecked")
private <O extends Objectable> PrismObject<O> parseObject(MapXNode map, PrismObjectDefinition<O> objectDefinition,
ParsingContext pc) throws SchemaException {
ItemInfo itemInfo = ItemInfo.determine(objectDefinition,
null, null, ARTIFICIAL_OBJECT_NAME,
map.getTypeQName(), null,
null, PrismObjectDefinition.class, pc, getSchemaRegistry());
return (PrismObject<O>) (Item) parseItemInternal(map, itemInfo.getItemName(), itemInfo.getItemDefinition(), pc);
}
@SuppressWarnings("unchecked")
Item<?, ?> parseItem(@NotNull RootXNode root,
ItemDefinition<?> itemDefinition, QName itemName, QName typeName, Class<?> typeClass,
@NotNull ParsingContext pc) throws SchemaException {
ItemInfo itemInfo = ItemInfo.determine(itemDefinition,
root.getRootElementName(), itemName, ARTIFICIAL_OBJECT_NAME,
root.getTypeQName(), typeName,
typeClass, ItemDefinition.class, pc, getSchemaRegistry());
ItemDefinition realDefinition;
if (itemInfo.getItemDefinition() == null && itemInfo.getComplexTypeDefinition() != null) {
// let's create container definition dynamically
QName actualTypeName = itemInfo.getComplexTypeDefinition().getTypeName();
if (getSchemaRegistry().isContainer(actualTypeName)) { // TODO what about objects?
PrismContainerDefinitionImpl def = new PrismContainerDefinitionImpl(itemInfo.getItemName(),
itemInfo.getComplexTypeDefinition(), prismContext);
def.setDynamic(true);
realDefinition = def;
} else {
PrismPropertyDefinitionImpl def = new PrismPropertyDefinitionImpl(itemInfo.getItemName(), actualTypeName, prismContext);
def.setDynamic(true);
realDefinition = def;
}
} else {
realDefinition = itemInfo.getItemDefinition();
}
return parseItemInternal(root.getSubnode(), itemInfo.getItemName(), realDefinition, pc);
}
Object parseItemOrRealValue(@NotNull RootXNode root, ParsingContext pc) throws SchemaException {
// is the type name explicitly specified? (if not, guess that we have a string)
QName typeName = root.getTypeQName();
if (typeName != null) {
ItemDefinition itemDefinition = getSchemaRegistry().findItemDefinitionByType(typeName);
if (itemDefinition != null) {
return parseItem(root, itemDefinition, null, null, null, pc);
} else {
return getBeanUnmarshaller().unmarshal(root, getSchemaRegistry().determineCompileTimeClass(typeName), pc);
}
} else {
// if type name is not known, we have to derive it from the element name
QName itemName = root.getRootElementName();
ItemDefinition itemDefinition = getSchemaRegistry().findItemDefinitionByElementName(itemName);
if (itemDefinition == null) {
throw new SchemaException("Couldn't parse general object with no type name and unknown element name: " + itemName);
}
return parseItem(root, itemDefinition, itemName, null, null, pc);
}
}
//endregion
//region Private methods ========================================================
// The situation of itemDefinition == null && node.typeName != null is allowed ONLY if the definition cannot be derived
// from the typeName. E.g. if typeName is like xsd:string, xsd:boolean, etc. This rule is because we don't want to repeatedly
// try to look for missing definitions here.
//
// So the caller is responsible for extracting information from node.typeQName - providing a definition if possible.
@SuppressWarnings("unchecked")
@NotNull
private Item<?, ?> parseItemInternal(@NotNull XNode node,
@NotNull QName itemName, ItemDefinition itemDefinition, @NotNull ParsingContext pc) throws SchemaException {
Validate.isTrue(!(node instanceof RootXNode));
// TODO execute this only if in checked mode
if (itemDefinition == null && node.getTypeQName() != null) {
PrismContainerDefinition<?> pcd = getSchemaRegistry().findContainerDefinitionByType(node.getTypeQName());
if (pcd != null) {
throw new IllegalStateException("Node has an explicit type corresponding to container (" + pcd
+ ") but parseItemInternal was called without definition: " + node.debugDump());
}
}
if (itemDefinition == null || itemDefinition instanceof PrismPropertyDefinition) {
return parseProperty(node, itemName, (PrismPropertyDefinition) itemDefinition, pc);
} else if (itemDefinition instanceof PrismContainerDefinition) { // also objects go here
return parseContainer(node, itemName, (PrismContainerDefinition<?>) itemDefinition, pc);
} else if (itemDefinition instanceof PrismReferenceDefinition) {
return parseReference(node, itemName, (PrismReferenceDefinition) itemDefinition, pc);
} else {
throw new IllegalArgumentException("Attempt to parse unknown definition type " + itemDefinition.getClass().getName());
}
}
@NotNull
private <C extends Containerable> PrismContainer<C> parseContainer(@NotNull XNode node, @NotNull QName itemName,
@NotNull PrismContainerDefinition<C> containerDef, @NotNull ParsingContext pc) throws SchemaException {
PrismContainer<C> container = containerDef.instantiate(itemName);
if (node instanceof ListXNode) {
ListXNode list = (ListXNode) node;
if (containerDef instanceof PrismObject && list.size() > 1) {
pc.warnOrThrow(LOGGER, "Multiple values for a PrismObject: " + node.debugDump());
parseContainerValueToContainer(container, list.get(0), pc);
} else {
for (XNode subNode : list) {
parseContainerValueToContainer(container, subNode, pc);
}
}
} else {
parseContainerValueToContainer(container, node, pc);
}
return container;
}
private <C extends Containerable> void parseContainerValueToContainer(PrismContainer<C> container, XNode node,
@NotNull ParsingContext pc) throws SchemaException {
container.add(parseContainerValue(node, container.getDefinition(), pc));
if (node instanceof MapXNode && container instanceof PrismObject) {
MapXNode map = (MapXNode) node;
PrismObject object = (PrismObject) container;
object.setOid(getOid(map));
object.setVersion(getVersion(map));
}
}
private String getOid(MapXNode xmap) throws SchemaException {
return xmap.getParsedPrimitiveValue(XNode.KEY_OID, DOMUtil.XSD_STRING);
}
private String getVersion(MapXNode xmap) throws SchemaException {
return xmap.getParsedPrimitiveValue(XNode.KEY_VERSION, DOMUtil.XSD_STRING);
}
private Long getContainerId(MapXNode xmap) throws SchemaException {
return xmap.getParsedPrimitiveValue(XNode.KEY_CONTAINER_ID, DOMUtil.XSD_LONG);
}
private <C extends Containerable> PrismContainerValue<C> parseContainerValue(@NotNull XNode node,
@NotNull PrismContainerDefinition<C> containerDef, @NotNull ParsingContext pc) throws SchemaException {
if (node instanceof MapXNode) {
return parseContainerValueFromMap((MapXNode) node, containerDef, pc);
} else if (node instanceof PrimitiveXNode) {
PrimitiveXNode<?> prim = (PrimitiveXNode<?>) node;
if (prim.isEmpty()) {
return containerDef.createValue();
} else {
throw new IllegalArgumentException("Cannot parse container value from (non-empty) " + node);
}
} else {
throw new IllegalArgumentException("Cannot parse container value from " + node);
}
}
private <C extends Containerable> PrismContainerValue<C> parseContainerValueFromMap(@NotNull MapXNode map,
@NotNull PrismContainerDefinition<C> containerDef, @NotNull ParsingContext pc) throws SchemaException {
Long id = getContainerId(map);
ComplexTypeDefinition complexTypeDefinition = containerDef.getComplexTypeDefinition();
PrismContainerValue<C> cval;
if (containerDef instanceof PrismObjectDefinition) {
cval = ((PrismObjectDefinition) containerDef).createValue();
} else {
// override container definition, if explicit type is specified
if (map.getTypeQName() != null) {
ComplexTypeDefinition specificDef = getSchemaRegistry().findComplexTypeDefinitionByType(map.getTypeQName());
if (specificDef != null) {
complexTypeDefinition = specificDef;
} else {
pc.warnOrThrow(LOGGER, "Unknown type " + map.getTypeQName() + " in " + map);
}
}
cval = new PrismContainerValue<>(null, null, null, id, complexTypeDefinition, prismContext);
}
for (Entry<QName, XNode> entry : map.entrySet()) {
QName itemName = entry.getKey();
if (itemName == null) {
throw new IllegalArgumentException("Null item name while parsing " + map.debugDump());
}
if (QNameUtil.match(itemName, XNode.KEY_CONTAINER_ID)) {
continue;
}
if (containerDef instanceof PrismObjectDefinition &&
(QNameUtil.match(itemName, XNode.KEY_OID) || QNameUtil.match(itemName, XNode.KEY_VERSION))) {
continue;
}
ItemDefinition itemDef = locateItemDefinition(itemName, complexTypeDefinition, entry.getValue());
if (itemDef == null) {
if (complexTypeDefinition == null || complexTypeDefinition.isXsdAnyMarker() || complexTypeDefinition.isRuntimeSchema()) {
PrismSchema itemSchema = getSchemaRegistry().findSchemaByNamespace(itemName.getNamespaceURI());
if (itemSchema != null) {
// If we already have schema for this namespace then a missing element is
// an error. We positively know that it is not in the schema.
pc.warnOrThrow(LOGGER, "Item " + itemName + " has no definition (schema present, in container "
+ containerDef + ")" + "while parsing " + map.debugDump());
continue;
} else {
// No definition for item, but the schema is runtime. the definition may come later.
// Null is OK here. The item will be parsed as "raw"
}
} else { // complex type definition is static
pc.warnOrThrow(LOGGER, "Item " + itemName + " has no definition (in container value "
+ complexTypeDefinition + ")" + "while parsing " + map.debugDump());
continue; // don't even attempt to parse it
}
}
Item<?, ?> item;
if (entry.getValue() == null) {
if (itemDef != null) {
item = itemDef.instantiate(); // TODO or skip the creation altogether?
} else {
item = null;
}
} else {
item = parseItemInternal(entry.getValue(), itemName, itemDef, pc);
}
// Merge must be here, not just add. Some items (e.g. references) have alternative
// names and representations and these cannot be processed as one map or list
if (item != null) {
cval.merge(item);
}
}
return cval;
}
@NotNull
private <T> PrismProperty<T> parseProperty(@NotNull XNode node, @NotNull QName itemName,
@Nullable PrismPropertyDefinition<T> itemDefinition, @NotNull ParsingContext pc) throws SchemaException {
Validate.isTrue(!(node instanceof RootXNode));
PrismProperty<T> property = itemDefinition != null ?
itemDefinition.instantiate() :
new PrismProperty<>(itemName, prismContext);
if (node instanceof ListXNode && !node.isHeterogeneousList()) {
ListXNode listNode = (ListXNode) node;
if (itemDefinition != null && !itemDefinition.isMultiValue() && listNode.size() > 1) {
throw new SchemaException("Attempt to store multiple values in single-valued property " + itemName);
}
for (XNode subNode : listNode) {
PrismPropertyValue<T> pval = parsePropertyValue(subNode, itemDefinition, pc);
addItemValueIfPossible(property, pval, pc);
}
} else if (node instanceof MapXNode || node instanceof PrimitiveXNode || node.isHeterogeneousList()) {
PrismPropertyValue<T> pval = parsePropertyValue(node, itemDefinition, pc);
if (pval != null) {
property.add(pval);
}
} else if (node instanceof SchemaXNode) {
SchemaDefinitionType schemaDefType = getBeanUnmarshaller().unmarshalSchemaDefinitionType((SchemaXNode) node);
@SuppressWarnings("unchecked")
PrismPropertyValue<T> val = new PrismPropertyValue(schemaDefType);
addItemValueIfPossible(property, val, pc);
} else {
throw new IllegalArgumentException("Cannot parse property from " + node);
}
return property;
}
private <V extends PrismValue, D extends ItemDefinition> void addItemValueIfPossible(Item<V, D> item, V value, ParsingContext pc) throws SchemaException {
if (value != null) {
try {
item.add(value);
} catch (SchemaException e) {
pc.warnOrThrow(LOGGER, "Couldn't add a value of " + value + " to the containing item: " + e.getMessage(), e);
}
}
}
// if definition == null or any AND node has type defined, this type must be non-containerable (fit into PPV)
private <T> PrismPropertyValue<T> parsePropertyValue(@NotNull XNode node,
@Nullable PrismPropertyDefinition<T> definition, @NotNull ParsingContext pc) throws SchemaException {
QName typeFromDefinition = definition != null && !definition.isAnyType() ? definition.getTypeName() : null;
QName typeName =
getSchemaRegistry().areComparable(typeFromDefinition, node.getTypeQName()) ?
getSchemaRegistry().selectMoreSpecific(typeFromDefinition, node.getTypeQName()) : null;
if (typeName == null) {
return createRawPrismPropertyValue(node);
} else if (getBeanUnmarshaller().canProcess(typeName)) {
T realValue = getBeanUnmarshaller().unmarshal(node, typeName, pc);
// Postprocessing after returning from unmarshaller. It speaks bean language (e.g. PolyStringType, not PolyString).
// It also doesn't know about prism-specific things like allowed values, etc.
if (realValue instanceof PolyStringType) {
@SuppressWarnings("unchecked")
T valueT = (T) ((PolyStringType) realValue).toPolyString();
realValue = valueT;
}
PrismUtil.recomputeRealValue(realValue, prismContext);
if (!isValueAllowed(realValue, definition)) {
pc.warnOrThrow(LOGGER, "Unknown (not allowed) value of type " + typeName + ". Value: " + realValue + ". Allowed values: " + definition.getAllowedValues());
return null;
}
if (realValue == null) {
return null;
}
PrismPropertyValue<T> ppv = new PrismPropertyValue<>(realValue);
ppv.setPrismContext(prismContext);
return ppv;
} else {
pc.warnOrThrow(LOGGER, "Cannot parse as " + typeName + ": " + node.debugDump());
return createRawPrismPropertyValue(node);
}
}
@NotNull
private <T> PrismPropertyValue<T> createRawPrismPropertyValue(@NotNull XNode node) {
PrismPropertyValue<T> ppv = PrismPropertyValue.createRaw(node);
ppv.setPrismContext(prismContext);
return ppv;
}
private <T> boolean isValueAllowed(T realValue, PrismPropertyDefinition<T> definition) throws SchemaException {
if (definition == null || CollectionUtils.isEmpty(definition.getAllowedValues())) {
return true;
}
if (realValue == null) {
return true; // TODO: ok?
}
String serializedForm;
if (realValue instanceof Enum) {
PrimitiveXNode<String> prim = (PrimitiveXNode<String>) getBeanMarshaller().marshall(realValue);
serializedForm = prim.getValue();
} else {
serializedForm = null;
}
return definition.getAllowedValues().stream()
.anyMatch(displayableValue ->
realValue.equals(displayableValue.getValue())
|| serializedForm != null && serializedForm.equals(displayableValue.getValue())
);
}
@NotNull
private PrismReference parseReference(@NotNull XNode node, @NotNull QName itemName,
@NotNull PrismReferenceDefinition definition, @NotNull ParsingContext pc) throws SchemaException {
PrismReference ref = definition.instantiate();
if (node instanceof ListXNode) {
ListXNode listNode = (ListXNode) node;
if (!definition.isMultiValue() && listNode.size() > 1) {
throw new SchemaException("Attempt to store multiple values in single-valued reference " + itemName);
}
for (XNode subNode : listNode) {
ref.add(parseReferenceValueFromXNode(subNode, definition, itemName, pc));
}
} else if (node instanceof MapXNode) {
ref.add(parseReferenceValueFromXNode(node, definition, itemName, pc));
} else if (node instanceof PrimitiveXNode) {
// empty
} else {
throw new IllegalArgumentException("Cannot parse reference from " + node);
}
return ref;
}
@NotNull
private PrismReferenceValue parseReferenceValueFromXNode(@NotNull XNode node,
@NotNull PrismReferenceDefinition definition, @NotNull QName itemName, @NotNull ParsingContext pc) throws SchemaException {
/*
* We distinguish between "real" references and composite objects by
* (1) looking at type QName of XNode passed (whether it's ObjectType or ObjectReferenceType)
* (2) comparing itemName and name from reference definition - e.g. linkRef vs. link
*/
boolean isComposite;
if (node.getTypeQName() != null) {
QName typeName = node.getTypeQName();
ItemDefinition contentDefinition = getSchemaRegistry().findItemDefinitionByType(typeName);
isComposite = contentDefinition instanceof PrismObjectDefinition;
} else {
isComposite = !QNameUtil.match(itemName, definition.getName());
}
if (isComposite) {
return parseReferenceValueAsCompositeObject(node, definition, pc); // This is a composite object (complete object stored inside reference)
} else {
// TODO fix this hack: for delta values of ObjectReferenceType we will not
// insist on having reference type (because the target definition could be such that it won't require it)
boolean allowMissingRefTypesOverride = node.isExplicitTypeDeclaration();
return parseReferenceValueAsReference(node, definition, pc, allowMissingRefTypesOverride); // This is "real" reference (oid, and nothing more)
}
}
private PrismReferenceValue parseReferenceValueAsReference(@NotNull XNode xnode, @NotNull PrismReferenceDefinition definition,
@NotNull ParsingContext pc, boolean allowMissingRefTypesOverride) throws SchemaException {
if (!(xnode instanceof MapXNode)) {
throw new IllegalArgumentException("Cannot parse reference from " + xnode);
}
MapXNode map = (MapXNode) xnode;
String oid = map.getParsedPrimitiveValue(XNode.KEY_REFERENCE_OID, DOMUtil.XSD_STRING);
PrismReferenceValue refVal = new PrismReferenceValue(oid);
QName type = map.getParsedPrimitiveValue(XNode.KEY_REFERENCE_TYPE, DOMUtil.XSD_QNAME);
if (type == null) {
if (!pc.isAllowMissingRefTypes() && !allowMissingRefTypesOverride) {
type = definition.getTargetTypeName();
if (type == null) {
throw new SchemaException("Target type in reference " + definition.getName() +
" not specified in reference nor in the schema");
}
}
} else {
if (QNameUtil.noNamespace(type)) {
type = getSchemaRegistry().resolveUnqualifiedTypeName(type);
}
QName defTargetType = definition.getTargetTypeName();
if (defTargetType != null) {
if (!(prismContext.getSchemaRegistry().isAssignableFrom(defTargetType, type))) {
throw new SchemaException("Target type specified in reference " + definition.getName() +
" (" + type + ") does not match target type in schema (" + defTargetType + ")");
}
}
}
PrismObjectDefinition<Objectable> objectDefinition = null;
if (type != null) {
objectDefinition = getSchemaRegistry().findObjectDefinitionByType(type);
if (objectDefinition == null) {
throw new SchemaException("No definition for type " + type + " in reference");
}
refVal.setTargetType(type);
}
QName relationAttribute = map.getParsedPrimitiveValue(XNode.KEY_REFERENCE_RELATION, DOMUtil.XSD_QNAME);
refVal.setRelation(relationAttribute);
refVal.setDescription(map.getParsedPrimitiveValue(XNode.KEY_REFERENCE_DESCRIPTION, DOMUtil.XSD_STRING));
refVal.setFilter(parseFilter(map.get(XNode.KEY_REFERENCE_FILTER)));
String resolutionTimeString = map.getParsedPrimitiveValue(XNode.KEY_REFERENCE_RESOLUTION_TIME, DOMUtil.XSD_STRING);
if (resolutionTimeString != null) {
EvaluationTimeType resolutionTime = EvaluationTimeType.fromValue(resolutionTimeString);
refVal.setResolutionTime(resolutionTime);
}
XNode xnodeForTargetName = map.get(XNode.KEY_REFERENCE_TARGET_NAME);
if (xnodeForTargetName != null) {
PolyStringType targetName = getBeanUnmarshaller().unmarshal(xnodeForTargetName, PolyStringType.class, pc);
refVal.setTargetName(targetName);
}
XNode xrefObject = map.get(XNode.KEY_REFERENCE_OBJECT);
if (xrefObject != null) {
if (!(xrefObject instanceof MapXNode)) {
throw new SchemaException("Cannot parse object from " + xrefObject);
}
if (type == null) {
throw new SchemaException("Cannot parse object from " + xrefObject + " without knowing its type");
}
PrismObject<Objectable> object = parseObject((MapXNode) xrefObject, objectDefinition, pc);
setReferenceObject(refVal, object);
}
return refVal;
}
private void setReferenceObject(PrismReferenceValue refVal, PrismObject<Objectable> object) throws SchemaException {
refVal.setObject(object);
if (object.getOid() != null) {
if (refVal.getOid() == null) {
refVal.setOid(object.getOid());
} else {
if (!refVal.getOid().equals(object.getOid())) {
throw new SchemaException("OID in reference (" + refVal.getOid() + ") does not match OID in composite object (" + object.getOid() + ")");
}
}
}
QName objectTypeName = object.getDefinition().getTypeName();
if (refVal.getTargetType() == null) {
refVal.setTargetType(objectTypeName);
} else {
if (!refVal.getTargetType().equals(objectTypeName)) {
throw new SchemaException("Target type in reference (" + refVal.getTargetType() + ") does not match type in composite object (" + objectTypeName + ")");
}
}
}
private PrismReferenceValue parseReferenceValueAsCompositeObject(XNode node,
PrismReferenceDefinition definition, ParsingContext pc) throws SchemaException {
if (!(node instanceof MapXNode)) {
throw new IllegalArgumentException("Cannot parse reference composite object from " + node);
}
MapXNode map = (MapXNode) node;
QName targetTypeName = definition.getTargetTypeName();
PrismObjectDefinition<Objectable> objectDefinition = null;
if (map.getTypeQName() != null) {
objectDefinition = getSchemaRegistry().findObjectDefinitionByType(map.getTypeQName());
}
if (objectDefinition == null && targetTypeName != null) {
objectDefinition = getSchemaRegistry().findObjectDefinitionByType(targetTypeName);
}
if (objectDefinition == null) {
throw new SchemaException("No object definition for composite object in reference element "
+ definition.getCompositeObjectElementName());
}
PrismObject<Objectable> compositeObject;
try {
compositeObject = parseObject(map, objectDefinition, pc);
} catch (SchemaException e) {
throw new SchemaException(e.getMessage() + " while parsing composite object in reference element "
+ definition.getCompositeObjectElementName(), e);
}
PrismReferenceValue refVal = new PrismReferenceValue();
setReferenceObject(refVal, compositeObject);
((PrismReferenceDefinitionImpl) definition).setComposite(true);
return refVal;
}
private SearchFilterType parseFilter(XNode xnode) throws SchemaException {
if (xnode == null) {
return null;
}
if (xnode.isEmpty()) {
return null;
}
return SearchFilterType.createFromXNode(xnode, prismContext);
}
private ItemDefinition locateItemDefinition(@NotNull QName itemName,
@Nullable ComplexTypeDefinition complexTypeDefinition,
XNode xnode) throws SchemaException {
return getSchemaRegistry()
.locateItemDefinition(itemName, complexTypeDefinition, qName -> resolveDynamicItemDefinition(qName, xnode));
}
private ItemDefinition resolveDynamicItemDefinition(QName itemName, XNode node) {
if (node == null) {
return null;
}
QName typeName = node.getTypeQName();
if (typeName == null) {
if (node instanceof ListXNode) {
// there may be type definitions in individual list members
for (XNode subNode : ((ListXNode) node)) {
ItemDefinition subdef = resolveDynamicItemDefinition(itemName, subNode);
// TODO: make this smarter, e.g. detect conflicting type definitions
if (subdef != null) {
return subdef;
}
}
}
}
if (typeName == null) {
return null;
}
PrismPropertyDefinitionImpl propDef = new PrismPropertyDefinitionImpl(itemName, typeName, prismContext);
Integer maxOccurs = node.getMaxOccurs();
if (maxOccurs != null) {
propDef.setMaxOccurs(maxOccurs);
} else {
// Make this multivalue by default, this is more "open"
propDef.setMaxOccurs(-1);
}
propDef.setDynamic(true);
return propDef;
}
//endregion
//endregion
private BeanUnmarshaller getBeanUnmarshaller() {
return ((PrismContextImpl) prismContext).getBeanUnmarshaller();
}
private BeanMarshaller getBeanMarshaller() {
return ((PrismContextImpl) prismContext).getBeanMarshaller();
}
private SchemaRegistry getSchemaRegistry() {
return prismContext.getSchemaRegistry();
}
//TODO
public <T extends Containerable> ItemDefinition locateItemDefinition(
@NotNull PrismContainerDefinition<T> containerDefinition, @NotNull QName itemName, @Nullable XNode xnode)
throws SchemaException {
return locateItemDefinition(itemName, containerDefinition.getComplexTypeDefinition(), xnode);
}
}