From a43635dc81fa585f35801783ac5b8e33d3949bfc Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Thu, 25 Jun 2020 09:59:25 +0200 Subject: [PATCH] Add DOM metadata handling With some code cleanup, e.g. removing deprecated parts of DOM writing. Relates to MID-6337 --- .../com/evolveum/midpoint/prism/Hacks.java | 2 - .../midpoint/prism/PrismConstants.java | 2 +- .../ns/_public/query_3/SearchFilterType.java | 7 - .../midpoint/prism/impl/HacksImpl.java | 12 - .../impl/lex/dom/DomLexicalProcessor.java | 40 +- .../prism/impl/lex/dom/DomLexicalWriter.java | 437 ------------------ .../prism/impl/lex/dom/DomReader.java | 121 +++-- .../prism/impl/lex/dom/DomWriter.java | 437 ++++++++++++++++++ .../midpoint/prism/PrismInternalTestUtil.java | 3 +- .../midpoint/prism/TestPrismParsing.java | 13 +- .../prism/query/TestQueryConverters.java | 117 +++-- .../common/xml/user-alice-metadata.xml | 3 + .../midpoint/schema/util/ObjectQueryUtil.java | 5 +- .../midpoint/schema/TestQueryConverter.java | 40 +- .../schema/parser/TestParseScriptOutput.java | 4 +- .../midpoint/schema/parser/TestParseUser.java | 2 +- .../parser/TestParseUserPolyString.java | 2 +- .../com/evolveum/midpoint/util/DOMUtil.java | 18 +- .../test/resources/importer/import-task.json | 2 +- 19 files changed, 646 insertions(+), 621 deletions(-) delete mode 100644 infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java create mode 100644 infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java index aa45b8e66fb..f46f94d9f17 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java @@ -25,6 +25,4 @@ public interface Hacks { @VisibleForTesting void parseProtectedType(ProtectedDataType protectedType, MapXNode xmap, PrismContext prismContext, ParsingContext pc) throws SchemaException; - - Element serializeSingleElementMapToElement(MapXNode filterClauseXNode) throws SchemaException; } diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java index 85eedc0fa4c..e983720b900 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java @@ -111,7 +111,7 @@ public class PrismConstants { public static final QName SCHEMA_APP_INFO = new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"); public static final QName A_MAX_OCCURS = new QName(NS_ANNOTATION, "maxOccurs"); - public static final String MULTIPLICITY_UNBONUNDED = "unbounded"; + public static final String MULTIPLICITY_UNBOUNDED = "unbounded"; public static final QName A_NAMESPACE = new QName(NS_ANNOTATION, "namespace"); public static final String A_NAMESPACE_PREFIX = "prefix"; diff --git a/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java b/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java index 18e43d27969..f57d5d239d1 100644 --- a/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java +++ b/infra/prism-api/src/main/java/com/evolveum/prism/xml/ns/_public/query_3/SearchFilterType.java @@ -119,13 +119,6 @@ public RootXNode getFilterClauseAsRootXNode() throws SchemaException { return clause != null ? clause.getSingleSubEntryAsRoot("getFilterClauseAsRootXNode") : null; } - public Element getFilterClauseAsElement(@NotNull PrismContext prismContext) throws SchemaException { - if (filterClauseXNode == null) { - return null; - } - return prismContext.hacks().serializeSingleElementMapToElement(filterClauseXNode); - } - public static SearchFilterType createFromParsedXNode(XNode xnode, ParsingContext pc, PrismContext prismContext) throws SchemaException { SearchFilterType filter = new SearchFilterType(); filter.parseFromXNode(xnode, pc, prismContext); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java index 61212ad6e94..7c00274b25a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java @@ -10,12 +10,10 @@ import javax.xml.namespace.QName; import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Element; import com.evolveum.midpoint.prism.Hacks; import com.evolveum.midpoint.prism.ParsingContext; import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; import com.evolveum.midpoint.prism.impl.marshaller.XNodeProcessorUtil; import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; @@ -58,16 +56,6 @@ public void parseProtectedType(ProtectedDataType protectedType, MapXNode XNodeProcessorUtil.parseProtectedType(protectedType, (MapXNodeImpl) xmap, prismContext, pc); } - @Override - public Element serializeSingleElementMapToElement(MapXNode filterClauseXNode) throws SchemaException { - DomLexicalProcessor domParser = getDomParser(prismContext); - return domParser.serializeSingleElementMapToElement(filterClauseXNode); - } - - private static DomLexicalProcessor getDomParser(@NotNull PrismContext prismContext) { - return ((PrismContextImpl) prismContext).getParserDom(); - } - @Override public void setXNodeType(XNode node, QName explicitTypeName, boolean explicitTypeDeclaration) { ((XNodeImpl) node).setTypeQName(explicitTypeName); diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java index 2cd06c92e5a..4248b698a58 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java @@ -49,8 +49,8 @@ public DomLexicalProcessor(@NotNull SchemaRegistry schemaRegistry) { @Override public RootXNodeImpl read(@NotNull ParserSource source, @NotNull ParsingContext parsingContext) throws SchemaException, IOException { if (source instanceof ParserElementSource) { - return new DomReader(((ParserElementSource) source).getElement(), schemaRegistry) - .read(); + Element root = ((ParserElementSource) source).getElement(); + return new DomReader(root, schemaRegistry).read(); } else { InputStream is = source.getInputStream(); try { @@ -103,47 +103,49 @@ public boolean canRead(@NotNull String dataString) { @NotNull @Override public String write(@NotNull XNode xnode, @NotNull QName rootElementName, SerializationContext serializationContext) throws SchemaException { - DomLexicalWriter writer = new DomLexicalWriter(schemaRegistry, serializationContext); RootXNodeImpl xroot = LexicalUtils.createRootXNode((XNodeImpl) xnode, rootElementName); - Element element = writer.write(xroot); + Element element = + new DomWriter(schemaRegistry, serializationContext) + .writeRoot(xroot); return DOMUtil.serializeDOMToString(element); } @NotNull @Override public String write(@NotNull RootXNode xnode, SerializationContext serializationContext) throws SchemaException { - DomLexicalWriter writer = new DomLexicalWriter(schemaRegistry, serializationContext); - Element element = writer.write((RootXNodeImpl) xnode); + Element element = + new DomWriter(schemaRegistry, serializationContext) + .writeRoot(xnode); return DOMUtil.serializeDOMToString(element); } @NotNull @Override public String write(@NotNull List roots, @Nullable SerializationContext context) throws SchemaException { - Element aggregateElement = writeXRootListToElement(roots); - return DOMUtil.serializeDOMToString(aggregateElement); + Element element = writeXRootListToElement(roots); + return DOMUtil.serializeDOMToString(element); } @NotNull public Element writeXRootListToElement(@NotNull List roots) throws SchemaException { - return new DomLexicalWriter(schemaRegistry, null) - .write(roots); + return new DomWriter(schemaRegistry, null) + .writeRoots(roots); } + /** + * Seems to be used in strange circumstances (called from various hacks). + * To be reconsidered eventually. Avoid using in new code. + */ + @Deprecated public Element writeXMapToElement(MapXNodeImpl xmap, QName elementName) throws SchemaException { - return new DomLexicalWriter(schemaRegistry, null) - .writeToElement(xmap, elementName); + return new DomWriter(schemaRegistry, null) + .writeMap(xmap, elementName); } @NotNull public Element writeXRootToElement(@NotNull RootXNodeImpl xroot) throws SchemaException { - return new DomLexicalWriter(schemaRegistry, null) - .write(xroot); - } - - public Element serializeSingleElementMapToElement(MapXNode map) throws SchemaException { - return new DomLexicalWriter(schemaRegistry, null) - .writeSingleElementMapToElement(map); + return new DomWriter(schemaRegistry, null) + .writeRoot(xroot); } // TODO move somewhere diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java deleted file mode 100644 index 48655c64d7a..00000000000 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (c) 2010-2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.prism.impl.lex.dom; - -import com.evolveum.midpoint.prism.SerializationContext; -import com.evolveum.midpoint.prism.SerializationOptions; -import com.evolveum.midpoint.prism.impl.PrismContextImpl; -import com.evolveum.midpoint.prism.impl.marshaller.ItemPathHolder; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.prism.xml.DynamicNamespacePrefixMapper; -import com.evolveum.midpoint.prism.xml.XsdTypeMapper; -import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.SchemaXNodeImpl; -import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; -import com.evolveum.midpoint.prism.xnode.IncompleteMarkerXNode; -import com.evolveum.midpoint.prism.xnode.MapXNode; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.Validate; -import org.jetbrains.annotations.NotNull; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; - -import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; - -/** - * @author semancik - * - */ -public class DomLexicalWriter { - - private Document doc; - private boolean serializeCompositeObjects = false; - private final SchemaRegistry schemaRegistry; - private final SerializationOptions serializationOptions; - - DomLexicalWriter(SchemaRegistry schemaRegistry, SerializationContext context) { - super(); - this.schemaRegistry = schemaRegistry; - this.serializationOptions = context != null ? context.getOptions() : null; - } - - public boolean isSerializeCompositeObjects() { - return serializeCompositeObjects; - } - - public void setSerializeCompositeObjects(boolean serializeCompositeObjects) { - this.serializeCompositeObjects = serializeCompositeObjects; - } - - private DynamicNamespacePrefixMapper getNamespacePrefixMapper() { - if (schemaRegistry == null) { - return null; - } - return schemaRegistry.getNamespacePrefixMapper(); - } - - private void initialize() { - doc = DOMUtil.getDocument(); - } - - private void initializeWithExistingDocument(Document document) { - doc = document; - document.getDocumentElement(); - } - - @NotNull - public Element write(@NotNull RootXNodeImpl rootxnode) throws SchemaException { - initialize(); - return serializeInternal(rootxnode, null); - } - - public Element write(@NotNull List roots) throws SchemaException { - initialize(); - QName aggregateElementName = schemaRegistry.getPrismContext().getObjectsElementName(); - if (aggregateElementName == null) { - throw new IllegalStateException("Couldn't serialize list of objects because the aggregated element name is not set"); - } - Element aggregateElement = createElement(aggregateElementName, null); - for (RootXNodeImpl root : roots) { - serializeInternal(root, aggregateElement); - } - return aggregateElement; - } - - public Element serializeUnderElement(RootXNodeImpl rootxnode, Element parentElement) throws SchemaException { - initializeWithExistingDocument(parentElement.getOwnerDocument()); - return serializeInternal(rootxnode, parentElement); - } - - @NotNull - private Element serializeInternal(@NotNull RootXNodeImpl rootxnode, Element parentElement) throws SchemaException { - QName rootElementName = rootxnode.getRootElementName(); - Element topElement = createElement(rootElementName, parentElement); - if (parentElement != null) { - parentElement.appendChild(topElement); - } - QName typeQName = rootxnode.getTypeQName(); - if (typeQName != null && rootxnode.isExplicitTypeDeclaration()) { - DOMUtil.setXsiType(topElement, setQNamePrefixExplicitIfNeeded(typeQName)); - } - XNodeImpl subnode = rootxnode.getSubnode(); - if (subnode instanceof PrimitiveXNodeImpl) { - serializePrimitiveElementOrAttribute((PrimitiveXNodeImpl) subnode, topElement, rootElementName, false); - return DOMUtil.getFirstChildElement(topElement); - } else { - if (subnode instanceof MapXNodeImpl) { - // at this point we can put frequently used namespaces (e.g. c, t, q, ri) into the document to eliminate their use - // on many places inside the doc (MID-2198) - DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); - serializeMap((MapXNodeImpl) subnode, topElement); - } else if (subnode.isHeterogeneousList()) { - DOMUtil.setNamespaceDeclarations(topElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); - serializeExplicitList((ListXNodeImpl) subnode, topElement); - } else { - throw new SchemaException( - "Sub-root xnode is not a map nor an explicit list, cannot serialize to XML (it is " + subnode + ")"); - } - addDefaultNamespaceDeclaration(topElement); - return topElement; - } - } - - /** - * Adds xmlns='...common-3' if needed. - * In fact, this is VERY ugly hack to avoid putting common-3 declarations all over the elements in bulk actions response. - * e.getNamespaceURI returns something only if it was present during node creation. - * So, in fact, this code is more than fragile. Seems to work with the current XNode->DOM serialization, though. - */ - private void addDefaultNamespaceDeclaration(Element top) { - List prefixless = DOMUtil.getElementsWithoutNamespacePrefix(top); - if (prefixless.size() < 2) { - return; // nothing to do here - } - Set namespaces = prefixless.stream().map(e -> emptyIfNull(e.getNamespaceURI())).collect(Collectors.toSet()); - if (namespaces.size() != 1 || StringUtils.isEmpty(namespaces.iterator().next())) { - return; - } - DOMUtil.setNamespaceDeclaration(top, "", namespaces.iterator().next()); - } - - Element writeToElement(MapXNodeImpl xmap, QName elementName) throws SchemaException { - initialize(); - Element element = createElement(elementName, null); - serializeMap(xmap, element); - return element; - } - - private void serializeMap(MapXNodeImpl xmap, Element topElement) throws SchemaException { - for (Entry entry: xmap.entrySet()) { - QName elementQName = entry.getKey(); - XNodeImpl xsubnode = entry.getValue(); - serializeSubnode(xsubnode, topElement, elementQName); - } - } - - private void serializeSubnode(XNodeImpl xsubnode, Element parentElement, QName elementName) throws SchemaException { - if (xsubnode == null) { - return; - } - if (xsubnode instanceof IncompleteMarkerXNode) { - Element element = createElement(elementName, parentElement); - DOMUtil.setAttributeValue(element, DOMUtil.IS_INCOMPLETE_ATTRIBUTE_NAME, "true"); - parentElement.appendChild(element); - } else if (xsubnode instanceof RootXNodeImpl) { - Element element = createElement(elementName, parentElement); - appendCommentIfPresent(element, xsubnode); - parentElement.appendChild(element); - serializeSubnode(((RootXNodeImpl) xsubnode).getSubnode(), element, ((RootXNodeImpl) xsubnode).getRootElementName()); - } else if (xsubnode instanceof MapXNodeImpl) { - Element element = createElement(elementName, parentElement); - appendCommentIfPresent(element, xsubnode); - if (xsubnode.isExplicitTypeDeclaration() && xsubnode.getTypeQName() != null) { - DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(xsubnode.getTypeQName())); - } - parentElement.appendChild(element); - serializeMap((MapXNodeImpl)xsubnode, element); - } else if (xsubnode instanceof PrimitiveXNodeImpl) { - PrimitiveXNodeImpl xprim = (PrimitiveXNodeImpl)xsubnode; - if (xprim.isAttribute()) { - serializePrimitiveElementOrAttribute(xprim, parentElement, elementName, true); - } else { - serializePrimitiveElementOrAttribute(xprim, parentElement, elementName, false); - } - } else if (xsubnode instanceof ListXNodeImpl) { - ListXNodeImpl xlist = (ListXNodeImpl)xsubnode; - if (xlist.isHeterogeneousList()) { - Element element = createElement(elementName, parentElement); - serializeExplicitList(xlist, element); - parentElement.appendChild(element); - } else { - for (XNodeImpl xsubsubnode : xlist) { - serializeSubnode(xsubsubnode, parentElement, elementName); - } - } - } else if (xsubnode instanceof SchemaXNodeImpl) { - serializeSchema((SchemaXNodeImpl)xsubnode, parentElement); - } else { - throw new IllegalArgumentException("Unknown subnode "+xsubnode); - } - } - - private void serializeExplicitList(ListXNodeImpl list, Element parent) throws SchemaException { - for (XNodeImpl node : list) { - if (node.getElementName() == null) { - throw new SchemaException("In a list, there are both nodes with element names and nodes without them: " + list); - } - serializeSubnode(node, parent, node.getElementName()); - } - DOMUtil.setAttributeValue(parent, DOMUtil.IS_LIST_ATTRIBUTE_NAME, "true"); - } - - private Element writeXPrimitiveToElement(PrimitiveXNodeImpl xprim, QName elementName) throws SchemaException { - initialize(); - Element parent = DOMUtil.createElement(doc, new QName("fake","fake")); - serializePrimitiveElementOrAttribute(xprim, parent, elementName, false); - return DOMUtil.getFirstChildElement(parent); - } - - private void serializePrimitiveElementOrAttribute(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, boolean asAttribute) throws SchemaException { - QName typeQName = xprim.getTypeQName(); - - // if typeQName is not explicitly specified, we try to determine it from parsed value - // TODO we should probably set typeQName when parsing the value... - if (typeQName == null && xprim.isParsed()) { - Object v = xprim.getValue(); - if (v != null) { - typeQName = XsdTypeMapper.toXsdType(v.getClass()); - } - } - - if (typeQName == null) { // this means that either xprim is unparsed or it is empty - if (PrismContextImpl.isAllowSchemalessSerialization()) { - // We cannot correctly serialize without a type. But this is needed - // sometimes. So just default to string - String stringValue = xprim.getStringValue(); - if (stringValue != null) { - if (asAttribute) { - DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), stringValue); - DOMUtil.setNamespaceDeclarations(parentElement, xprim.getRelevantNamespaceDeclarations()); - } else { - Element element; - try { - element = createElement(elementOrAttributeName, parentElement); - appendCommentIfPresent(element, xprim); - } catch (DOMException e) { - throw new DOMException(e.code, e.getMessage() + "; creating element "+elementOrAttributeName+" in element "+DOMUtil.getQName(parentElement)); - } - parentElement.appendChild(element); - DOMUtil.setElementTextContent(element, stringValue); - DOMUtil.setNamespaceDeclarations(element, xprim.getRelevantNamespaceDeclarations()); - } - } - return; - } else { - throw new IllegalStateException("No type for primitive element "+elementOrAttributeName+", cannot serialize (schemaless serialization is disabled)"); - } - } - - // typeName != null after this point - - if (QNameUtil.isUnqualified(typeQName)) { - typeQName = XsdTypeMapper.determineQNameWithNs(typeQName); - } - - Element element = null; - - if (ItemPathType.COMPLEX_TYPE.equals(typeQName)) { - //ItemPathType itemPathType = //ItemPathType.asItemPathType(xprim.getValue()); // TODO fix this hack - ItemPathType itemPathType = (ItemPathType) xprim.getValue(); - if (itemPathType != null) { - if (asAttribute) { - throw new UnsupportedOperationException("Serializing ItemPath as an attribute is not supported yet"); - } - element = serializeItemPathTypeToElement(itemPathType, elementOrAttributeName, parentElement.getOwnerDocument()); - parentElement.appendChild(element); - } - - } else { - // not an ItemPathType - - if (!asAttribute) { - try { - element = createElement(elementOrAttributeName, parentElement); - } catch (DOMException e) { - throw new DOMException(e.code, e.getMessage() + "; creating element "+elementOrAttributeName+" in element "+DOMUtil.getQName(parentElement)); - } - appendCommentIfPresent(element, xprim); - parentElement.appendChild(element); - } - - if (DOMUtil.XSD_QNAME.equals(typeQName)) { - QName value = (QName) xprim.getParsedValueWithoutRecording(DOMUtil.XSD_QNAME); - value = setQNamePrefixExplicitIfNeeded(value); - if (asAttribute) { - try { - DOMUtil.setQNameAttribute(parentElement, elementOrAttributeName.getLocalPart(), value); - } catch (DOMException e) { - throw new DOMException(e.code, e.getMessage() + "; setting attribute "+elementOrAttributeName.getLocalPart()+" in element "+DOMUtil.getQName(parentElement)+" to QName value "+value); - } - } else { - DOMUtil.setQNameValue(element, value); - } - } else { - // not ItemType nor QName - String value = xprim.getGuessedFormattedValue(); - String fixedValue = SerializationOptions.isEscapeInvalidCharacters(serializationOptions) ? - DOMUtil.escapeInvalidXmlCharsIfPresent(value) : value; - if (asAttribute) { - DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), fixedValue); - } else { - DOMUtil.setElementTextContent(element, fixedValue); - } - } - - } - if (!asAttribute && xprim.isExplicitTypeDeclaration()) { - DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(typeQName)); - } - } - - private void appendCommentIfPresent(Element element, XNodeImpl xnode) { - String text = xnode.getComment(); - if (StringUtils.isNotEmpty(text)) { - DOMUtil.createComment(element, text); - } - } - - private void serializeSchema(SchemaXNodeImpl xschema, Element parentElement) { - Element schemaElement = xschema.getSchemaElement(); - if (schemaElement == null){ - return; - } - Element clonedSchema = (Element) schemaElement.cloneNode(true); - doc.adoptNode(clonedSchema); - parentElement.appendChild(clonedSchema); - } - - /** - * Create XML element with the correct namespace prefix and namespace definition. - * @param qname element QName - * @return created DOM element - */ - @NotNull - private Element createElement(QName qname, Element parentElement) { - String namespaceURI = qname.getNamespaceURI(); - if (!StringUtils.isBlank(namespaceURI)) { - qname = setQNamePrefix(qname); - } - if (parentElement != null) { - return DOMUtil.createElement(doc, qname, parentElement, parentElement); - } else { - // This is needed otherwise the root element itself could not be created - // Caller of this method is responsible for setting the topElement - return DOMUtil.createElement(doc, qname); - } - } - - private QName setQNamePrefix(QName qname) { - DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); - if (namespacePrefixMapper == null) { - return qname; - } - return namespacePrefixMapper.setQNamePrefix(qname); - } - - private QName setQNamePrefixExplicitIfNeeded(QName name) { - if (name != null && StringUtils.isNotBlank(name.getNamespaceURI()) && StringUtils.isBlank(name.getPrefix())) { - return setQNamePrefixExplicit(name); - } else { - return name; - } - } - - private QName setQNamePrefixExplicit(QName qname) { - DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); - if (namespacePrefixMapper == null) { - return qname; - } - return namespacePrefixMapper.setQNamePrefixExplicit(qname); - } - - private Element serializeItemPathTypeToElement(ItemPathType itemPathType, QName elementName, Document ownerDocument) { - return ItemPathHolder.serializeToElement(itemPathType.getItemPath(), elementName, ownerDocument); - } - - Element writeSingleElementMapToElement(MapXNode map) throws SchemaException { - MapXNodeImpl xmap = (MapXNodeImpl) map; - if (xmap == null || xmap.isEmpty()) { - return null; - } - Entry subEntry = xmap.getSingleSubEntry(xmap.toString()); - Element parent = writeToElement2(xmap, subEntry.getKey()); - return DOMUtil.getFirstChildElement(parent); - } - - // TODO correlate to writeToElement - private Element writeToElement2(XNodeImpl xnode, QName elementName) throws SchemaException { - Validate.notNull(xnode); - Validate.notNull(elementName); - if (xnode instanceof MapXNodeImpl) { - return writeToElement((MapXNodeImpl) xnode, elementName); - } else if (xnode instanceof PrimitiveXNodeImpl) { - return writeXPrimitiveToElement((PrimitiveXNodeImpl) xnode, elementName); - } else if (xnode instanceof RootXNodeImpl) { - return write((RootXNodeImpl) xnode); - } else if (xnode instanceof ListXNodeImpl) { - ListXNodeImpl xlist = (ListXNodeImpl) xnode; - if (xlist.size() == 0) { - return null; - } else if (xlist.size() > 1) { - throw new IllegalArgumentException("Cannot serialize list xnode with more than one item: " + xlist); - } else { - return writeToElement2(xlist.get(0), elementName); - } - } else { - throw new IllegalArgumentException("Cannot serialize " + xnode + " to element"); - } - } -} diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java index cef9fd163e8..72d8d8bea8e 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomReader.java @@ -12,6 +12,8 @@ import com.evolveum.midpoint.prism.impl.xnode.*; import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.MetadataAware; import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; @@ -40,14 +42,22 @@ class DomReader { public static final Trace LOGGER = TraceManager.getTrace(DomLexicalProcessor.class); private static final QName SCHEMA_ELEMENT_QNAME = DOMUtil.XSD_SCHEMA_ELEMENT; - private static final String VALUE_LOCAL_PART = "_value"; + static final String VALUE_LOCAL_PART = "_value"; + static final String METADATA_LOCAL_PART = "_metadata"; private final Element root; + private final QName rootElementName; private final SchemaRegistry schemaRegistry; + @NotNull private final QName valueElementName; + @NotNull private final QName metadataElementName; + DomReader(@NotNull Element root, SchemaRegistry schemaRegistry) { this.root = root; + this.rootElementName = DOMUtil.getQName(root); this.schemaRegistry = schemaRegistry; + this.valueElementName = new QName(schemaRegistry.getDefaultNamespace(), VALUE_LOCAL_PART); + this.metadataElementName = new QName(schemaRegistry.getDefaultNamespace(), DomReader.METADATA_LOCAL_PART); } DomReader(Document document, SchemaRegistry schemaRegistry) { @@ -56,8 +66,7 @@ class DomReader { @NotNull List readObjects() throws SchemaException { - QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); - if (objectsMarker != null && !QNameUtil.match(DOMUtil.getQName(root), objectsMarker)) { + if (rootIsNotObjectsMarker()) { return Collections.singletonList(read()); } else { List rv = new ArrayList<>(); @@ -69,22 +78,69 @@ List readObjects() throws SchemaException { } @NotNull RootXNodeImpl read() throws SchemaException { - QName rootElementName = DOMUtil.getQName(root); - RootXNodeImpl xroot = new RootXNodeImpl(rootElementName); XNodeImpl xnode = readElementContent(root, rootElementName, false); xroot.setSubnode(xnode); return xroot; } - private void extractCommonMetadata(Element element, QName xsiType, XNodeImpl xnode) - throws SchemaException { + private boolean rootIsNotObjectsMarker() { + QName objectsMarker = schemaRegistry.getPrismContext().getObjectsElementName(); + return objectsMarker != null && !QNameUtil.match(rootElementName, objectsMarker); + } - if (xsiType != null) { - xnode.setTypeQName(xsiType); - xnode.setExplicitTypeDeclaration(true); + /** + * Reads the content of the element. + * + * @param knownElementName Pre-fetched element name. Might be null (this is expected if storeElementName is true). + */ + @NotNull + private XNodeImpl readElementContent(@NotNull Element element, QName knownElementName, boolean storeElementName) throws SchemaException { + XNodeImpl node; + + QName xsiType = DOMUtil.resolveXsiType(element); + QName elementName = knownElementName != null ? knownElementName : DOMUtil.getQName(element); + + Element valueChild = DOMUtil.getMatchingChildElement(element, valueElementName); + if (valueChild != null) { + node = readElementContent(valueChild, elementName, false); + } else if (DOMUtil.hasChildElements(element) || DOMUtil.hasApplicationAttributes(element)) { + if (isList(element, elementName, xsiType)) { + node = readElementContentToList(element); + } else { + node = readElementContentToMap(element); + } + } else if (DOMUtil.isMarkedAsIncomplete(element)) { + // Note that it is of no use to check for "incomplete" on non-leaf elements. In XML the incomplete attribute + // must be attached to an empty element. + node = new IncompleteMarkerXNodeImpl(); + } else { + node = parsePrimitiveElement(element); } + readMetadata(element, node); + readMaxOccurs(element, node); + + setTypeAndElementName(xsiType, elementName, node, storeElementName); + return node; + } + private void readMetadata(@NotNull Element element, XNodeImpl node) throws SchemaException { + Element metadataChild = DOMUtil.getMatchingChildElement(element, metadataElementName); + if (metadataChild != null) { + XNodeImpl metadata = readElementContent(metadataChild, null, false); + if (metadata instanceof MapXNode) { + if (node instanceof MetadataAware) { + ((MetadataAware) node).setMetadataNode((MapXNode) metadata); + } else { + throw new SchemaException("Attempt to add metadata to non-metadata-aware XNode: " + node); + } + } else { + throw new SchemaException("Metadata is not of Map type: " + metadata); + } + } + } + + private void readMaxOccurs(Element element, XNodeImpl xnode) throws SchemaException { String maxOccursString = element.getAttributeNS( PrismConstants.A_MAX_OCCURS.getNamespaceURI(), PrismConstants.A_MAX_OCCURS.getLocalPart()); @@ -95,7 +151,7 @@ private void extractCommonMetadata(Element element, QName xsiType, XNodeImpl xno } private int parseMultiplicity(String maxOccursString, Element element) throws SchemaException { - if (PrismConstants.MULTIPLICITY_UNBONUNDED.equals(maxOccursString)) { + if (PrismConstants.MULTIPLICITY_UNBOUNDED.equals(maxOccursString)) { return -1; } if (maxOccursString.startsWith("-")) { @@ -109,51 +165,25 @@ private int parseMultiplicity(String maxOccursString, Element element) throws Sc } } - /** - * Reads the content of the element. - * - * @param knownElementName Pre-fetched element name. Might be null (this is expected if storeElementName is true). - */ - @NotNull - private XNodeImpl readElementContent(@NotNull Element element, QName knownElementName, boolean storeElementName) throws SchemaException { - XNodeImpl node; - - QName xsiType = DOMUtil.resolveXsiType(element); - QName elementName = knownElementName != null ? knownElementName : DOMUtil.getQName(element); - -// Element valueChild = DOMUtil.getChildElement(element, VALUE_LOCAL_PART); -// if (valueChild != null) { -// node = readElementContent(valueChild, elementName, false); -// } else - if (DOMUtil.hasChildElements(element) || DOMUtil.hasApplicationAttributes(element)) { - if (isList(element, elementName, xsiType)) { - node = parseElementContentToList(element); - } else { - node = parseElementContentToMap(element); - } - } else if (DOMUtil.isMarkedAsIncomplete(element)) { - // Note that it is of no use to check for "incomplete" on non-leaf elements. In XML the incomplete attribute - // must be attached to an empty element. - node = new IncompleteMarkerXNodeImpl(); - } else { - node = parsePrimitiveElement(element); + private void setTypeAndElementName(QName xsiType, QName elementName, XNodeImpl xnode, boolean storeElementName) { + if (xsiType != null) { + xnode.setTypeQName(xsiType); + xnode.setExplicitTypeDeclaration(true); } if (storeElementName) { - node.setElementName(elementName); + xnode.setElementName(elementName); } - extractCommonMetadata(element, xsiType, node); - return node; } // all the sub-elements should be compatible (this is not enforced here, however) - private ListXNodeImpl parseElementContentToList(Element element) throws SchemaException { + private ListXNodeImpl readElementContentToList(Element element) throws SchemaException { if (DOMUtil.hasApplicationAttributes(element)) { throw new SchemaException("List should have no application attributes: " + element); } return parseElementList(DOMUtil.listChildElements(element), null, true); } - private MapXNodeImpl parseElementContentToMap(Element element) throws SchemaException { + private MapXNodeImpl readElementContentToMap(Element element) throws SchemaException { MapXNodeImpl xmap = new MapXNodeImpl(); // Attributes @@ -168,6 +198,9 @@ private MapXNodeImpl parseElementContentToMap(Element element) throws SchemaExce List lastElements = null; for (Element childElement : DOMUtil.listChildElements(element)) { QName childName = DOMUtil.getQName(childElement); + if (QNameUtil.match(childName, metadataElementName)) { + continue; + } if (!match(childName, lastElementName)) { parseSubElementsGroupAsMapEntry(xmap, lastElementName, lastElements); lastElementName = childName; diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java new file mode 100644 index 00000000000..2227fd87b72 --- /dev/null +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomWriter.java @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.prism.impl.lex.dom; + +import com.evolveum.midpoint.prism.SerializationContext; +import com.evolveum.midpoint.prism.SerializationOptions; +import com.evolveum.midpoint.prism.impl.PrismContextImpl; +import com.evolveum.midpoint.prism.impl.marshaller.ItemPathHolder; +import com.evolveum.midpoint.prism.schema.SchemaRegistry; +import com.evolveum.midpoint.prism.xml.DynamicNamespacePrefixMapper; +import com.evolveum.midpoint.prism.xml.XsdTypeMapper; +import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.RootXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.SchemaXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; +import com.evolveum.midpoint.prism.xnode.IncompleteMarkerXNode; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.prism.xnode.RootXNode; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.namespace.QName; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.evolveum.midpoint.prism.impl.lex.dom.DomReader.VALUE_LOCAL_PART; +import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; + +/** + * @author semancik + * + */ +class DomWriter { + + @NotNull private final Document document; + @NotNull private final SchemaRegistry schemaRegistry; + private final SerializationOptions serializationOptions; + + @NotNull private final QName valueElementName; + @NotNull private final QName metadataElementName; + + DomWriter(@NotNull SchemaRegistry schemaRegistry, SerializationContext context) { + this.document = DOMUtil.getDocument(); + this.schemaRegistry = schemaRegistry; + this.serializationOptions = context != null ? context.getOptions() : null; + this.valueElementName = new QName(schemaRegistry.getDefaultNamespace(), VALUE_LOCAL_PART); + this.metadataElementName = new QName(schemaRegistry.getDefaultNamespace(), DomReader.METADATA_LOCAL_PART); + } + + @NotNull + Element writeRoot(@NotNull RootXNode root) throws SchemaException { + return writeRootInternal((RootXNodeImpl) root, null); + } + + Element writeRoots(@NotNull List roots) throws SchemaException { + QName aggregateElementName = schemaRegistry.getPrismContext().getObjectsElementName(); + if (aggregateElementName == null) { + throw new IllegalStateException("Couldn't serialize list of objects because the aggregated element name is not set"); + } + Element aggregateElement = createElement(aggregateElementName, null); + for (RootXNodeImpl root : roots) { + writeRootInternal(root, aggregateElement); + } + return aggregateElement; + } + + /** + * Seems to be used in strange circumstances (called from various hacks). + * To be reconsidered eventually. Avoid using in new code. + */ + @Deprecated + Element writeMap(MapXNodeImpl xmap, QName elementName) throws SchemaException { + Element element = createElement(elementName, null); + writeMap(xmap, element); + return element; + } + + /** + * TODO There is an overlap in functionality between this method and writeNode. + * We should reconcile that somehow eventually. + * But beware, there are also significant differences, e.g. + * - we set "global" namespace declarations and default namespace declaration here + * - when serializing primitives we insist on element representation (not attribute one) + */ + @NotNull + private Element writeRootInternal(@NotNull RootXNodeImpl root, Element parentElement) throws SchemaException { + QName rootElementName = root.getRootElementName(); + Element rootElement = createAndAppendChild(rootElementName, parentElement); + + XNodeImpl subnode = root.getSubnode(); + if (subnode instanceof PrimitiveXNodeImpl) { + writePrimitive((PrimitiveXNodeImpl) subnode, rootElement, rootElementName, false); + return DOMUtil.getFirstChildElement(rootElement); + } else { + // At this point we can put frequently used namespaces (e.g. c, t, q, ri) into the document to eliminate their use + // on many places inside the doc (MID-2198) + DOMUtil.setNamespaceDeclarations(rootElement, getNamespacePrefixMapper().getNamespacesDeclaredByDefault()); + if (subnode instanceof MapXNodeImpl) { + writeMap((MapXNodeImpl) subnode, rootElement); + } else if (subnode.isHeterogeneousList()) { + writeHeterogeneousList((ListXNodeImpl) subnode, rootElement); + } else { + throw new SchemaException( + "Sub-root xnode is not a map nor an explicit list, cannot serialize to XML (it is " + subnode + ")"); + } + addDefaultNamespaceDeclaration(rootElement); + return rootElement; + } + } + + /** + * Adds xmlns='...common-3' if needed. + * In fact, this is VERY ugly hack to avoid putting common-3 declarations all over the elements in bulk actions response. + * e.getNamespaceURI returns something only if it was present during node creation. + * So, in fact, this code is more than fragile. Seems to work with the current XNode->DOM serialization, though. + */ + private void addDefaultNamespaceDeclaration(Element top) { + List prefixLess = DOMUtil.getElementsWithoutNamespacePrefix(top); + if (prefixLess.size() < 2) { + return; // nothing to do here + } + Set namespaces = prefixLess.stream() + .map(e -> emptyIfNull(e.getNamespaceURI())) + .collect(Collectors.toSet()); + if (namespaces.size() == 1) { + String defaultNamespace = namespaces.iterator().next(); + if (StringUtils.isNotEmpty(defaultNamespace)) { + DOMUtil.setNamespaceDeclaration(top, "", defaultNamespace); + } + } + } + + private void writeMap(MapXNodeImpl xmap, Element parent) throws SchemaException { + MapXNode metadataNode = xmap.getMetadataNode(); + if (metadataNode != null) { + writeMetadata(metadataNode, parent); + } + for (Entry entry: xmap.entrySet()) { + QName elementQName = entry.getKey(); + XNodeImpl xsubnode = entry.getValue(); + writeNode(xsubnode, parent, elementQName); + } + setXsiTypeIfNeeded(xmap, parent); + } + + private void writeNode(XNodeImpl node, Element parentElement, QName elementName) throws SchemaException { + if (node == null) { + return; + } + if (node instanceof IncompleteMarkerXNode) { + Element child = createAndAppendChild(elementName, parentElement); + DOMUtil.setAttributeValue(child, DOMUtil.IS_INCOMPLETE_ATTRIBUTE_NAME, "true"); + } else if (node instanceof RootXNodeImpl) { + throw new IllegalStateException("Shouldn't be here!"); + // TODO remove eventually +// Element child = createAndAppendChild(elementName, parentElement); +// appendCommentIfPresent(child, node); +// writeNode(((RootXNodeImpl) node).getSubnode(), child, ((RootXNodeImpl) node).getRootElementName()); + } else if (node instanceof MapXNodeImpl) { + Element child = createAndAppendChild(elementName, parentElement); + appendCommentIfPresent(child, node); + writeMap((MapXNodeImpl)node, child); + } else if (node instanceof PrimitiveXNodeImpl) { + PrimitiveXNodeImpl xprim = (PrimitiveXNodeImpl)node; + writePrimitive(xprim, parentElement, elementName, xprim.isAttribute()); + } else if (node instanceof ListXNodeImpl) { + ListXNodeImpl list = (ListXNodeImpl) node; + if (list.isHeterogeneousList()) { + Element child = createAndAppendChild(elementName, parentElement); + writeHeterogeneousList(list, child); + } else { + for (XNodeImpl listItem : list) { + writeNode(listItem, parentElement, elementName); + } + } + } else if (node instanceof SchemaXNodeImpl) { + writeSchema((SchemaXNodeImpl)node, parentElement); + } else { + throw new IllegalArgumentException("Unknown subnode "+node); + } + } + + private void writeHeterogeneousList(ListXNodeImpl list, Element parent) throws SchemaException { + for (XNodeImpl listItem : list) { + if (listItem.getElementName() == null) { + throw new SchemaException("In a list, there are both nodes with element names and nodes without them: " + list); + } + writeNode(listItem, parent, listItem.getElementName()); + } + DOMUtil.setAttributeValue(parent, DOMUtil.IS_LIST_ATTRIBUTE_NAME, "true"); + setXsiTypeIfNeeded(list, parent); + } + + private void writePrimitive(PrimitiveXNodeImpl xprim, Element parentElement, + QName elementOrAttributeName, boolean asAttribute) throws SchemaException { + if (xprim.hasMetadata()) { + Element valuePlusMetadataElement = createAndAppendChild(elementOrAttributeName, parentElement); + writePrimitiveValue(xprim, valuePlusMetadataElement, valueElementName, false); + writeMetadata(xprim.getMetadataNode(), valuePlusMetadataElement); + } else { + writePrimitiveValue(xprim, parentElement, elementOrAttributeName, asAttribute); + } + } + + private void writeMetadata(MapXNode metadataNode, Element parentElement) throws SchemaException { + Element metadataElement = createAndAppendChild(metadataElementName, parentElement); + writeMap((MapXNodeImpl) metadataNode, metadataElement); + } + + private void writePrimitiveValue(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, + boolean asAttribute) throws SchemaException { + QName typeName = determineTypeName(xprim); + + if (typeName == null) { // this means that either xprim is unparsed or it is empty + writePrimitiveWithoutType(xprim, parentElement, elementOrAttributeName, asAttribute); + } else { + writePrimitiveWithType(xprim, parentElement, elementOrAttributeName, asAttribute, typeName); + } + } + + private void writePrimitiveWithoutType(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, + boolean asAttribute) { + if (!PrismContextImpl.isAllowSchemalessSerialization()) { + throw new IllegalStateException("No type for primitive element "+elementOrAttributeName+", cannot serialize (schemaless serialization is disabled)"); + } + + // We cannot correctly serialize without a type. But this is needed sometimes. So just default to string. + String stringValue = xprim.getStringValue(); + if (stringValue != null) { + if (asAttribute) { + DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), stringValue); + DOMUtil.setNamespaceDeclarations(parentElement, xprim.getRelevantNamespaceDeclarations()); + } else { + Element element = createAndAppendChild(elementOrAttributeName, parentElement); + appendCommentIfPresent(element, xprim); + DOMUtil.setElementTextContent(element, stringValue); + DOMUtil.setNamespaceDeclarations(element, xprim.getRelevantNamespaceDeclarations()); + } + } + } + + private void writePrimitiveWithType(PrimitiveXNodeImpl xprim, Element parentElement, QName elementOrAttributeName, + boolean asAttribute, QName typeName) throws SchemaException { + Element element; + // Note that item paths and QNames are special because of prefixes. + if (ItemPathType.COMPLEX_TYPE.equals(typeName)) { + if (asAttribute) { + throw new UnsupportedOperationException("Serializing ItemPath as an attribute is not supported yet"); + } else { + element = writeItemPath(xprim, parentElement, elementOrAttributeName); + } + } else { + if (!asAttribute) { + element = createAndAppendChild(elementOrAttributeName, parentElement); + appendCommentIfPresent(element, xprim); + } else { + element = null; + } + + if (DOMUtil.XSD_QNAME.equals(typeName)) { + QName value = (QName) xprim.getParsedValueWithoutRecording(DOMUtil.XSD_QNAME); + QName valueWithPrefix = setQNamePrefixExplicitIfNeeded(value); + if (asAttribute) { + setQNameAttribute(parentElement, elementOrAttributeName.getLocalPart(), valueWithPrefix); + } else { + DOMUtil.setQNameValue(element, valueWithPrefix); + } + } else { + // not ItemType nor QName + String value = xprim.getGuessedFormattedValue(); + String fixedValue = SerializationOptions.isEscapeInvalidCharacters(serializationOptions) ? + DOMUtil.escapeInvalidXmlCharsIfPresent(value) : value; + if (asAttribute) { + DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), fixedValue); + } else { + DOMUtil.setElementTextContent(element, fixedValue); + } + } + } + if (!asAttribute) { + setXsiTypeIfNeeded(xprim, element, typeName); + } + } + + private void setQNameAttribute(Element parentElement, String attributeName, QName value) { + try { + DOMUtil.setQNameAttribute(parentElement, attributeName, value); + } catch (DOMException e) { + throw new DOMException(e.code, e.getMessage() + "; setting attribute "+attributeName+" in element "+DOMUtil.getQName(parentElement)+" to QName value "+value); + } + } + + @Nullable + private Element writeItemPath(PrimitiveXNodeImpl xprim, Element parent, QName elementName) { + ItemPathType itemPathType = (ItemPathType) xprim.getValue(); + if (itemPathType != null) { + Element element = ItemPathHolder.serializeToElement(itemPathType.getItemPath(), elementName, document); + parent.appendChild(element); + return element; + } else { + return null; + } + } + + @Nullable + private QName determineTypeName(PrimitiveXNodeImpl xprim) { + QName explicitTypeName = xprim.getTypeQName(); + if (explicitTypeName != null) { + return qualifyIfNeeded(explicitTypeName); + } + + // Ff typeQName is not explicitly specified, we try to determine it from parsed value. + // We should probably set typeQName when parsing the value... + if (xprim.isParsed()) { + Object v = xprim.getValue(); + if (v != null) { + return XsdTypeMapper.toXsdType(v.getClass()); + } + } + + return null; + } + + @Nullable + private QName qualifyIfNeeded(QName explicitTypeName) { + if (QNameUtil.isUnqualified(explicitTypeName)) { + return XsdTypeMapper.determineQNameWithNs(explicitTypeName); + } else { + return explicitTypeName; + } + } + + private void appendCommentIfPresent(Element element, XNodeImpl xnode) { + String text = xnode.getComment(); + if (StringUtils.isNotEmpty(text)) { + DOMUtil.createComment(element, text); + } + } + + private void writeSchema(SchemaXNodeImpl xschema, Element parentElement) { + Element schemaElement = xschema.getSchemaElement(); + if (schemaElement != null) { + Element clonedSchemaElement = (Element) schemaElement.cloneNode(true); + document.adoptNode(clonedSchemaElement); + parentElement.appendChild(clonedSchemaElement); + } + } + + /** + * Create XML element with the correct namespace prefix and namespace definition. + * @param qname element QName + * @return created DOM element + */ + @NotNull + private Element createElement(QName qname, Element parentElement) { + try { + String namespaceURI = qname.getNamespaceURI(); + if (!StringUtils.isBlank(namespaceURI)) { + qname = setQNamePrefix(qname); + } + if (parentElement != null) { + return DOMUtil.createElement(document, qname, parentElement, parentElement); + } else { + // This is needed otherwise the root element itself could not be created + // Caller of this method is responsible for setting the topElement + return DOMUtil.createElement(document, qname); + } + } catch (DOMException e) { + throw new DOMException(e.code, e.getMessage() + "; creating element " + qname + " in element " + DOMUtil.getQName(parentElement)); + } + } + + @NotNull + private Element createAndAppendChild(QName childName, Element parent) { + Element child = createElement(childName, parent); + if (parent != null) { + parent.appendChild(child); + } + return child; + } + + private QName setQNamePrefix(QName qname) { + DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); + if (namespacePrefixMapper == null) { + return qname; + } + return namespacePrefixMapper.setQNamePrefix(qname); + } + + private QName setQNamePrefixExplicitIfNeeded(QName name) { + if (name != null && StringUtils.isNotBlank(name.getNamespaceURI()) && StringUtils.isBlank(name.getPrefix())) { + return setQNamePrefixExplicit(name); + } else { + return name; + } + } + + private QName setQNamePrefixExplicit(QName qname) { + DynamicNamespacePrefixMapper namespacePrefixMapper = getNamespacePrefixMapper(); + if (namespacePrefixMapper == null) { + return qname; + } + return namespacePrefixMapper.setQNamePrefixExplicit(qname); + } + + private DynamicNamespacePrefixMapper getNamespacePrefixMapper() { + return schemaRegistry.getNamespacePrefixMapper(); + } + + private void setXsiTypeIfNeeded(@NotNull XNodeImpl node, Element element) { + if (node.getTypeQName() != null && node.isExplicitTypeDeclaration()) { + DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(node.getTypeQName())); + } + } + + private void setXsiTypeIfNeeded(@NotNull XNodeImpl node, Element element, QName typeName) { + if (node.isExplicitTypeDeclaration()) { + DOMUtil.setXsiType(element, setQNamePrefixExplicitIfNeeded(typeName)); + } + } +} diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java index c92a296a596..1b001e8cdd4 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java @@ -235,6 +235,7 @@ public static PrismContextImpl constructPrismContext() throws SchemaException, F public static PrismContextImpl constructPrismContext(File extraSchema) throws SchemaException, FileNotFoundException { SchemaRegistryImpl schemaRegistry = new SchemaRegistryImpl(); schemaRegistry.setCatalogResourceName(TEST_CATALOG_RESOURCE_NAME); + schemaRegistry.setDefaultNamespace(NS_FOO); DynamicNamespacePrefixMapper prefixMapper = new GlobalDynamicNamespacePrefixMapper(); // Set default namespace? schemaRegistry.setNamespacePrefixMapper(prefixMapper); @@ -243,7 +244,7 @@ public static PrismContextImpl constructPrismContext(File extraSchema) throws Sc schemaRegistry.registerPrismSchemaResource("xml/ns/public/types-3.xsd", "t", com.evolveum.prism.xml.ns._public.types_3.ObjectFactory.class.getPackage()); schemaRegistry.registerPrismSchemaResource("xml/ns/public/query-3.xsd", "q", com.evolveum.prism.xml.ns._public.query_3.ObjectFactory.class.getPackage()); schemaRegistry.registerPrismSchemasFromDirectory(SCHEMA_DIR); - if (extraSchema != null){ + if (extraSchema != null) { schemaRegistry.registerPrismSchemaFile(extraSchema); } prefixMapper.registerPrefix(XMLConstants.W3C_XML_SCHEMA_NS_URI, DOMUtil.NS_W3C_XML_SCHEMA_PREFIX, false); diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java index 635f859a822..84fdd2cc53d 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsing.java @@ -388,7 +388,7 @@ public void test600AccountBarbossa() throws Exception { )), names); } - @Test(enabled = false) + @Test public void test700UserAliceMetadata() throws Exception { given(); PrismContext prismContext = getPrismContext(); @@ -399,9 +399,9 @@ public void test700UserAliceMetadata() throws Exception { assertAliceMetadata(root); - //testSerializeMetadata(root, PrismContext.LANG_XML); - testSerializeMetadata(root, PrismContext.LANG_JSON); - testSerializeMetadata(root, PrismContext.LANG_YAML); + assertAliceMetadata(testSerializeMetadata(root, PrismContext.LANG_XML)); + assertAliceMetadata(testSerializeMetadata(root, PrismContext.LANG_JSON)); + assertAliceMetadata(testSerializeMetadata(root, PrismContext.LANG_YAML)); } private void assertAliceMetadata(RootXNode alice) throws SchemaException { @@ -432,15 +432,14 @@ private void assertSingleMetadata(XNode node, String name, String expected) thro assertThat(parsedValue).isEqualTo(expected); } - private void testSerializeMetadata(RootXNode original, String language) throws SchemaException { + private RootXNode testSerializeMetadata(RootXNode original, String language) throws SchemaException { PrismContext prismContext = getPrismContext(); String serialized = prismContext.serializerFor(language).serialize(original); displayValue("serialized", serialized); RootXNode reparsed = prismContext.parserFor(serialized).parseToXNode(); displayValue("reparsed", reparsed); - - assertThat(reparsed).as("reparsed").isEqualTo(original); + return reparsed; } protected void assertUserAdhoc(PrismObject user, boolean expectRawInConstructions, boolean withIncomplete) throws SchemaException { diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java index 3b3e3b42dc0..2345e44cd06 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/query/TestQueryConverters.java @@ -73,18 +73,17 @@ public void testFilterUserNameJaxb() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - - System.out.println("Serialized filter (JAXB->DOM)"); - System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); - - DomAsserts.assertElementQName(filterClauseElement, EqualFilter.ELEMENT_NAME); - DomAsserts.assertSubElements(filterClauseElement, 2); - - DomAsserts.assertSubElement(filterClauseElement, PrismConstants.Q_VALUE); - Element valueElement = DOMUtil.getChildElement(filterClauseElement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(valueElement, "jack"); - +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); +// +// DomAsserts.assertElementQName(filterClauseElement, EqualFilter.ELEMENT_NAME); +// DomAsserts.assertSubElements(filterClauseElement, 2); +// +// DomAsserts.assertSubElement(filterClauseElement, PrismConstants.Q_VALUE); +// Element valueElement = DOMUtil.getChildElement(filterClauseElement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(valueElement, "jack"); } @Test @@ -122,22 +121,22 @@ public void testFilterUserAndJaxb() throws Exception { ListXNodeImpl xequalsList = (ListXNodeImpl) xandmap.get(EqualFilter.ELEMENT_NAME); PrismAsserts.assertSize(xequalsList, 2); - Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); - System.out.println("Serialized filter (JAXB->DOM)"); - System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); - - DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); - DomAsserts.assertSubElements(filterClauseElement, 2); - - Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); - DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); - Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(firstValueElement, "Jack"); - - Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); - DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); - Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(secondValueElement, "Caribbean"); +// Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); +// System.out.println("Serialized filter (JAXB->DOM)"); +// System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); +// +// DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); +// DomAsserts.assertSubElements(filterClauseElement, 2); +// +// Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); +// DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); +// Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(firstValueElement, "Jack"); +// +// Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); +// DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); +// Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(secondValueElement, "Caribbean"); } @Test @@ -159,15 +158,15 @@ public void testFilterTypeUserNone() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); - - System.out.println("Serialized filter (JAXB->DOM)"); - String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); - System.out.println(filterAsString); - logger.info(filterAsString); - - DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "type")); +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); +// System.out.println(filterAsString); +// logger.info(filterAsString); +// +// DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "type")); } @Test @@ -189,17 +188,17 @@ public void testFilterNotInOid() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); - - System.out.println("Serialized filter (JAXB->DOM)"); - String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); - System.out.println(filterAsString); - logger.info(filterAsString); - - DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); - assertEquals("wrong # of inOid subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "inOid").getLength()); - assertEquals("wrong # of value subfilters", 4, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); +// System.out.println(filterAsString); +// logger.info(filterAsString); +// +// DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); +// assertEquals("wrong # of inOid subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "inOid").getLength()); +// assertEquals("wrong # of value subfilters", 4, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); } @Test @@ -221,17 +220,17 @@ public void testFilterNotFullText() throws Exception { System.out.println("Re-converted query type"); System.out.println(convertedQueryType.debugDump()); - Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); - logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); - - System.out.println("Serialized filter (JAXB->DOM)"); - String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); - System.out.println(filterAsString); - logger.info(filterAsString); - - DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); - assertEquals("wrong # of fullText subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "fullText").getLength()); - assertEquals("wrong # of value subfilters", 2, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); +// Element filterClauseElement = convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()); +// logger.info(convertedQueryType.getFilter().getFilterClauseXNode().debugDump()); +// +// System.out.println("Serialized filter (JAXB->DOM)"); +// String filterAsString = DOMUtil.serializeDOMToString(filterClauseElement); +// System.out.println(filterAsString); +// logger.info(filterAsString); +// +// DomAsserts.assertElementQName(filterClauseElement, new QName(PrismConstants.NS_QUERY, "not")); +// assertEquals("wrong # of fullText subfilters", 1, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "fullText").getLength()); +// assertEquals("wrong # of value subfilters", 2, filterClauseElement.getElementsByTagNameNS(PrismConstants.NS_QUERY, "value").getLength()); } private ObjectQuery toObjectQuery(Class type, SearchFilterType filterType) throws Exception { diff --git a/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml b/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml index ddf25952e3b..85f22eafb14 100644 --- a/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml +++ b/infra/prism-impl/src/test/resources/common/xml/user-alice-metadata.xml @@ -16,6 +16,9 @@ xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3" xmlns:adhoc="http://midpoint.evolveum.com/xml/ns/test/adhoc-1.xsd" xmlns:ext="http://midpoint.evolveum.com/xml/ns/test/extension"> + <_metadata> + abc + <_value>alice <_metadata> diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java index 1be8d7fb8b7..22c582dc5b2 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectQueryUtil.java @@ -20,6 +20,7 @@ import com.evolveum.midpoint.prism.query.*; import com.evolveum.midpoint.prism.query.Visitor; import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit; +import com.evolveum.midpoint.prism.xnode.RootXNode; import com.evolveum.midpoint.schema.RelationRegistry; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.collections4.CollectionUtils; @@ -30,7 +31,6 @@ import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer; import com.evolveum.midpoint.prism.impl.polystring.AlphanumericPolyStringNormalizer; import com.evolveum.midpoint.schema.ResourceShadowDiscriminator; -import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.prism.xml.ns._public.query_3.QueryType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; @@ -242,7 +242,8 @@ public static String dump(QueryType query, @NotNull PrismContext prismContext) t StringBuilder sb = new StringBuilder("Query("); sb.append(query.getDescription()).append("):\n"); if (query.getFilter() != null && query.getFilter().containsFilterClause()) { - sb.append(DOMUtil.serializeDOMToString(query.getFilter().getFilterClauseAsElement(prismContext))); + RootXNode clause = query.getFilter().getFilterClauseAsRootXNode(); + sb.append(prismContext.xmlSerializer().serialize(clause)); } else { sb.append("(no filter)"); } diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java index 20351d533c1..14831a05540 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestQueryConverter.java @@ -111,22 +111,22 @@ public void testAccountFilter() throws Exception { ListXNode xequalsList = (ListXNode) xandmap.get(EqualFilter.ELEMENT_NAME); PrismAsserts.assertSize(xequalsList, 2); - Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); - System.out.println("Serialized filter (JAXB->DOM)"); - System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); - - DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); - DomAsserts.assertSubElements(filterClauseElement, 2); - - Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); - DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); - Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(firstValueElement, "abc"); - - Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); - DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); - Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); - DomAsserts.assertTextContent(secondValueElement, "someName"); +// Element filterClauseElement = convertedFilterType.getFilterClauseAsElement(getPrismContext()); +// System.out.println("Serialized filter (JAXB->DOM)"); +// System.out.println(DOMUtil.serializeDOMToString(filterClauseElement)); +// +// DomAsserts.assertElementQName(filterClauseElement, AndFilter.ELEMENT_NAME); +// DomAsserts.assertSubElements(filterClauseElement, 2); +// +// Element firstSubelement = DOMUtil.getChildElement(filterClauseElement, 0); +// DomAsserts.assertElementQName(firstSubelement, EqualFilter.ELEMENT_NAME); +// Element firstValueElement = DOMUtil.getChildElement(firstSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(firstValueElement, "abc"); +// +// Element secondSubelement = DOMUtil.getChildElement(filterClauseElement, 1); +// DomAsserts.assertElementQName(secondSubelement, EqualFilter.ELEMENT_NAME); +// Element secondValueElement = DOMUtil.getChildElement(secondSubelement, PrismConstants.Q_VALUE); +// DomAsserts.assertTextContent(secondValueElement, "someName"); } @Test @@ -150,7 +150,7 @@ public void testAccountQueryAttributesAndResource() throws Exception { PrismAsserts.assertEqualsFilterValue((EqualFilter) second, "uid=jbond,ou=People,dc=example,dc=com"); QueryType convertedQueryType = toQueryType(query); - logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } @Test @@ -174,7 +174,7 @@ public void testAccountQueryAttributesAndResourceNoNs() throws Exception { //PrismAsserts.assertEqualsFilterValue((EqualFilter) second, "uid=jbond,ou=People,dc=example,dc=com"); QueryType convertedQueryType = toQueryType(query); - System.out.println(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// System.out.println(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } @Test @@ -208,7 +208,7 @@ public void testAccountQueryCompositeOr() throws Exception { assertRefFilterValue((RefFilter) forth, "d0db5be9-cb93-401f-b6c1-86ffffe4cd5e"); QueryType convertedQueryType = toQueryType(query); - logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } @Test @@ -385,7 +385,7 @@ public void testUserQuery() throws Exception { System.out.println("QUERY Pretty print: " + query.toString()); QueryType convertedQueryType = getQueryConverter().createQueryType(query); - logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); +// logger.info(DOMUtil.serializeDOMToString(convertedQueryType.getFilter().getFilterClauseAsElement(getPrismContext()))); } catch (Exception ex) { logger.error("Error while converting query: {}", ex.getMessage(), ex); throw ex; diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java index a1d8bd30bba..0529207774a 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseScriptOutput.java @@ -72,8 +72,8 @@ public void testNamespaces() throws Exception { final String common_3 = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; int count = StringUtils.countMatches(serialized, common_3); - // temporarily set from 2 to 4 (some XML weirdness in Xerces 2.12/Xalan 2.7.2); see MID-5661 - assertEquals("Wrong # of occurrences of '" + common_3 + "' in serialized form", 4, count); + // temporarily set from 2 to 3 (some XML weirdness in Xerces 2.12/Xalan 2.7.2); see MID-5661 + assertEquals("Wrong # of occurrences of '" + common_3 + "' in serialized form", 3, count); } // private void assertNamespaceDeclarations(String context, Element element, String... prefixes) { diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java index a8c12270fe6..e45780f2696 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUser.java @@ -251,7 +251,7 @@ private void assertUserJaxb(UserType userType, boolean isObject) throws SchemaEx assertEquals("Wrong ref3 type (jaxb)", ShadowType.COMPLEX_TYPE, ref3.getType()); SearchFilterType ref3Filter = ref3.getFilter(); assertNotNull("No ref3 filter (jaxb,class)", ref3Filter); - assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); +// assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); } @Test diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java index 8e5589d3949..5e1904f6bd5 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/parser/TestParseUserPolyString.java @@ -275,7 +275,7 @@ private void assertUserJaxb(UserType userType, boolean isObject) throws SchemaEx assertEquals("Wrong ref3 type (jaxb)", ShadowType.COMPLEX_TYPE, ref3.getType()); SearchFilterType ref3Filter = ref3.getFilter(); assertNotNull("No ref3 filter (jaxb,class)", ref3Filter); - assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); +// assertFilterElement("ref filter (jaxb)", ref3Filter.getFilterClauseAsElement(getPrismContext())); } @Test diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java index 95fe3c1d770..847abcb1bb2 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java @@ -764,11 +764,10 @@ public static Map getNamespaceDeclarations(Element element) { } public static void setNamespaceDeclarations(Element element, Map rootNamespaceDeclarations) { - if (rootNamespaceDeclarations == null) { - return; - } - for (Entry entry : rootNamespaceDeclarations.entrySet()) { - setNamespaceDeclaration(element, entry.getKey(), entry.getValue()); + if (rootNamespaceDeclarations != null) { + for (Entry entry : rootNamespaceDeclarations.entrySet()) { + setNamespaceDeclaration(element, entry.getKey(), entry.getValue()); + } } } @@ -953,6 +952,15 @@ public static Element getChildElement(Element element, QName qname) { return null; } + public static Element getMatchingChildElement(Element element, QName qname) { + for (Element subelement: listChildElements(element)) { + if (QNameUtil.match(qname, getQName(subelement))) { + return subelement; + } + } + return null; + } + public static Element getNamedElement(List elements, QName qname) { for (Element element : elements) { if (qname.equals(getQName(element))) { diff --git a/model/model-intest/src/test/resources/importer/import-task.json b/model/model-intest/src/test/resources/importer/import-task.json index 61ec041826d..1d4a15235ae 100644 --- a/model/model-intest/src/test/resources/importer/import-task.json +++ b/model/model-intest/src/test/resources/importer/import-task.json @@ -21,4 +21,4 @@ "binding": "tight" } } -] \ No newline at end of file +]