Skip to content

Commit

Permalink
DomWriter: Use ItemPathSerialization for item path serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
tonydamage committed Feb 2, 2021
1 parent 4809d50 commit 85f0f89
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 23 deletions.
Expand Up @@ -245,14 +245,21 @@ private void writePrimitiveWithoutType(PrimitiveXNodeImpl<?> xprim, Element pare
// We cannot correctly serialize without a type. But this is needed sometimes. So just default to string.
String stringValue = xprim.getStringValue();
if (stringValue != null) {
// FIXME: DOMUtil.allNamespaceDeclarations does not return default namespace if document is constructed
PrismNamespaceContext currentNs = PrismNamespaceContext.from(DOMUtil.allNamespaceDeclarations(parentElement));
PrismNamespaceContext relevantNs = PrismNamespaceContext.from(xprim.getRelevantNamespaceDeclarations());
Element element;
if (asAttribute) {
DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), stringValue);
DOMUtil.setNamespaceDeclarations(parentElement, xprim.getRelevantNamespaceDeclarations());
element = parentElement;
} else {
Element element = createAndAppendChild(elementOrAttributeName, parentElement);
element = createAndAppendChild(elementOrAttributeName, parentElement);
appendCommentIfPresent(element, xprim);
DOMUtil.setElementTextContent(element, stringValue);
DOMUtil.setNamespaceDeclarations(element, xprim.getRelevantNamespaceDeclarations());
}
PrismNamespaceContext rebased = relevantNs.rebasedOn(currentNs);
if(!rebased.isLocalEmpty()) {
DOMUtil.setNamespaceDeclarations(element, rebased.localPrefixes());
}
}
}
Expand Down Expand Up @@ -320,7 +327,7 @@ private Element writeItemPath(PrimitiveXNodeImpl<?> xprim, Element parent, QName
Map<String, String> availableNamespaces = DOMUtil.getAllNonDefaultNamespaceDeclarations(parent);
PrismNamespaceContext localNs = PrismNamespaceContext.from(availableNamespaces);

ItemPathSerialization ctx = ItemPathSerialization.serialize(UniformItemPath.from(itemPathType.getItemPath()), localNs);
ItemPathSerialization ctx = ItemPathSerialization.serialize(UniformItemPath.from(itemPathType.getItemPath()), localNs, true);

Element element = createAndAppendChild(elementName, parent);
DOMUtil.setNamespaceDeclarations(element,ctx.undeclaredPrefixes());
Expand Down
Expand Up @@ -8,14 +8,14 @@
package com.evolveum.midpoint.prism.impl.marshaller;

import java.util.*;

import javax.xml.namespace.QName;

import org.jetbrains.annotations.NotNull;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismNamespaceContext;
import com.evolveum.midpoint.prism.PrismNamespaceContext.PrefixPreference;
import com.evolveum.midpoint.prism.path.*;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
Expand Down Expand Up @@ -89,6 +89,10 @@ public String getXPathWithAllDeclarations() {
}

public static ItemPathSerialization serialize(@NotNull UniformItemPath itemPath, PrismNamespaceContext context) {
return serialize(itemPath, context, false);
}

public static ItemPathSerialization serialize(@NotNull UniformItemPath itemPath, PrismNamespaceContext context, boolean overrideNs) {
Map<String, String> usedPrefixToNs = new HashMap<String, String>();
BiMap<String, String> undeclaredNsToPrefix = HashBiMap.create();

Expand All @@ -98,10 +102,10 @@ public static ItemPathSerialization serialize(@NotNull UniformItemPath itemPath,
PathHolderSegment xsegment;
if (segment instanceof NameItemPathSegment) {
QName name = ((NameItemPathSegment) segment).getName();
xsegment = new PathHolderSegment(assignPrefix(name, context, undeclaredNsToPrefix, usedPrefixToNs));
xsegment = new PathHolderSegment(assignPrefix(name, context, undeclaredNsToPrefix, usedPrefixToNs, overrideNs));
} else if (segment instanceof VariableItemPathSegment) {
QName name = ((VariableItemPathSegment) segment).getName();
xsegment = new PathHolderSegment(assignPrefix(name, context, undeclaredNsToPrefix, usedPrefixToNs), true);
xsegment = new PathHolderSegment(assignPrefix(name, context, undeclaredNsToPrefix, usedPrefixToNs, overrideNs), true);
} else if (segment instanceof IdItemPathSegment) {
xsegment = new PathHolderSegment(idToString(((IdItemPathSegment) segment).getId()));
} else if (segment instanceof ObjectReferencePathSegment) {
Expand All @@ -123,41 +127,74 @@ public static ItemPathSerialization serialize(@NotNull UniformItemPath itemPath,
}

private static QName assignPrefix(@NotNull QName name, PrismNamespaceContext global,
Map<String, String> localNamespaceToPrefix, Map<String, String> prefixToNs) {
Map<String, String> localNamespaceToPrefix, Map<String, String> prefixToNs, boolean overrideNs) {
String namespace = name.getNamespaceURI();
String explicitPrefix = name.getPrefix();
if(Strings.isNullOrEmpty(namespace)) {
Preconditions.checkState(Strings.isNullOrEmpty(explicitPrefix), "QName %s has prefix, but no namespace", name);
if(Strings.isNullOrEmpty(explicitPrefix)) {
/*
* COMPAT: QName has prefix, but no namespace, fallback to default namespace
* since we do not know how to interpret it
*/
return new ItemName(name.getLocalPart());
}
return name;
}

String proposedPrefix = assignPrefix(namespace, explicitPrefix, global, localNamespaceToPrefix, prefixToNs, overrideNs);
if(explicitPrefix.equals(proposedPrefix)) {
return name;
}
return new ItemName(namespace, name.getLocalPart(), proposedPrefix);

}

private static String assignPrefix(String namespace, String explicitPrefix, PrismNamespaceContext global,
Map<String, String> localNamespaceToPrefix, Map<String, String> prefixToNs, boolean overrideNs) {

// First we try to use existing prefix
if(!Strings.isNullOrEmpty(explicitPrefix)) {
String localNs = prefixToNs.get(explicitPrefix);
if(namespace.equals(localNs)) {
return explicitPrefix;
}
Optional<String> globalNs = global.namespaceFor(explicitPrefix);
if(globalNs.isPresent() && namespace.equals(globalNs.get())) {
prefixToNs.put(explicitPrefix, namespace);
return explicitPrefix;
}
if(overrideNs && localNs == null) {
localNamespaceToPrefix.putIfAbsent(namespace, explicitPrefix);
prefixToNs.put(explicitPrefix, namespace);
return explicitPrefix;
}
}
// Renaming item/prefix
String localPrefix = localNamespaceToPrefix.get(namespace);
if(localPrefix != null) {
return new QName(namespace, name.getLocalPart(), localPrefix);
// We already created local prefix for specified namespace
return localPrefix;
}
Optional<String> documentPrefix = global.prefixFor(namespace, PrefixPreference.GLOBAL_FIRST_SKIP_DEFAULTS);
if(documentPrefix.isPresent()) {
// Rename item (use document prefix)
prefixToNs.put(documentPrefix.get(), namespace);
return new QName(namespace, name.getLocalPart(), documentPrefix.get());
Optional<String> globalPrefix = global.prefixFor(namespace, PrefixPreference.GLOBAL_FIRST_SKIP_DEFAULTS);
if(globalPrefix.isPresent()) {
// We are reusing inherited prefix
prefixToNs.put(globalPrefix.get(), namespace);
return globalPrefix.get();
}

// Lookup if prefix was not assigned locally already

// We assign prefix
// We Try to compute new prefix
localPrefix = explicitPrefix;
while(isPrefixConflicting(localPrefix, prefixToNs, global)) {
localPrefix = proposeNewPrefix(namespace, localPrefix);
localPrefix = proposeNewPrefix(namespace, explicitPrefix);
}

prefixToNs.put(localPrefix, namespace);
localNamespaceToPrefix.put(namespace, localPrefix);

return new QName(namespace, name.getLocalPart(), localPrefix);
return localPrefix;
}

private static String proposeNewPrefix(String ns, String candidate) {
// FIXME Determine better assignment of random prefixes
return "gen" + new Random().nextInt(999);
return (Strings.isNullOrEmpty(candidate) ? "gen" : candidate) + new Random().nextInt(999);
}

private static boolean isPrefixConflicting(String candidate, Map<String, String> prefixToNs,
Expand Down

0 comments on commit 85f0f89

Please sign in to comment.