Skip to content

Commit

Permalink
Add minor DOM parsing performance improvements
Browse files Browse the repository at this point in the history
These are just cosmetics.

The only one functional change is dropping support for xsi:nil
attribute. It was not used by midPoint XML serialization anyway.

(cherry picked from commit 6b2dab1)
  • Loading branch information
mederly committed Jan 23, 2020
1 parent 5cd58a6 commit 797415f
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 59 deletions.
Expand Up @@ -33,27 +33,22 @@
class DomLessValueParser<T> implements ValueParser<T>, Serializable {

private final String textContent;
private final boolean isNil;
private final Map<String, String> visibleNamespaceDeclarations;

DomLessValueParser(Element element) {
textContent = element.getTextContent();
isNil = DOMUtil.isNil(element);
visibleNamespaceDeclarations = Collections.unmodifiableMap(DOMUtil.getAllVisibleNamespaceDeclarations(element));
}

DomLessValueParser(Attr attribute) {
textContent = attribute.getTextContent();
isNil = false;
visibleNamespaceDeclarations = Collections.unmodifiableMap(DOMUtil.getAllVisibleNamespaceDeclarations(attribute));
}

@Override
public T parse(QName typeName, XNodeProcessorEvaluationMode mode) throws SchemaException {
try {
if (isNil) {
return null;
} else if (ItemPathType.COMPLEX_TYPE.equals(typeName)) {
if (ItemPathType.COMPLEX_TYPE.equals(typeName)) {
//noinspection unchecked
return (T) new ItemPathType(ItemPathHolder.parseFromString(textContent, visibleNamespaceDeclarations));
} else if (XmlTypeConverter.canConvert(typeName)) {
Expand All @@ -72,7 +67,7 @@ public T parse(QName typeName, XNodeProcessorEvaluationMode mode) throws SchemaE

@Override
public boolean isEmpty() {
return isNil || StringUtils.isBlank(textContent);
return StringUtils.isBlank(textContent);
}

@Override
Expand Down
Expand Up @@ -175,14 +175,13 @@ public RootXNodeImpl read(Document document) throws SchemaException {
@NotNull
public RootXNodeImpl read(Element rootElement) throws SchemaException {
RootXNodeImpl xroot = new RootXNodeImpl(DOMUtil.getQName(rootElement));
extractCommonMetadata(rootElement, xroot);
extractCommonMetadata(rootElement, DOMUtil.resolveXsiType(rootElement), xroot);
XNodeImpl xnode = parseElementContent(rootElement, false);
xroot.setSubnode(xnode);
return xroot;
}

private void extractCommonMetadata(Element element, XNodeImpl xnode) throws SchemaException {
QName xsiType = DOMUtil.resolveXsiType(element);
private void extractCommonMetadata(Element element, QName xsiType, XNodeImpl xnode) throws SchemaException {
if (xsiType != null) {
xnode.setTypeQName(xsiType);
xnode.setExplicitTypeDeclaration(true);
Expand Down Expand Up @@ -216,14 +215,13 @@ private int parseMultiplicity(String maxOccursString, Element element) throws Sc
* Parses the content of the element. The name of the provided element is ignored (unless storeElementName=true),
* only the content is parsed.
*/
@Nullable
@NotNull
private XNodeImpl parseElementContent(@NotNull Element element, boolean storeElementName) throws SchemaException {
if (DOMUtil.isNil(element)) { // TODO: ok?
return null;
}
XNodeImpl node;

QName xsiType = DOMUtil.resolveXsiType(element);
if (DOMUtil.hasChildElements(element) || DOMUtil.hasApplicationAttributes(element)) {
if (isList(element)) {
if (isList(element, xsiType)) {
node = parseElementContentToList(element);
} else {
node = parseElementContentToMap(element);
Expand All @@ -234,7 +232,7 @@ private XNodeImpl parseElementContent(@NotNull Element element, boolean storeEle
if (storeElementName) {
node.setElementName(DOMUtil.getQName(element));
}
extractCommonMetadata(element, node);
extractCommonMetadata(element, xsiType, node);
return node;
}

Expand Down Expand Up @@ -272,18 +270,17 @@ private MapXNodeImpl parseElementContentToMap(Element element) throws SchemaExce
return xmap;
}

private boolean isList(Element element) throws SchemaException {
private boolean isList(Element element, QName typeName) {
String isListAttribute = DOMUtil.getAttribute(element, new QName(DOMUtil.IS_LIST_ATTRIBUTE_NAME));
if (StringUtils.isNotEmpty(isListAttribute)) {
return Boolean.valueOf(isListAttribute);
return Boolean.parseBoolean(isListAttribute);
}
// enable this after schema registry is optional (now it's mandatory)
// if (schemaRegistry == null) {
// return false;
// }

// checking parent element fitness
QName typeName = DOMUtil.resolveXsiType(element);
if (typeName != null) {
Collection<? extends ComplexTypeDefinition> definitions = schemaRegistry
.findTypeDefinitionsByType(typeName, ComplexTypeDefinition.class);
Expand Down
Expand Up @@ -34,7 +34,7 @@
*/
public class AbstractSchemaPerformanceTest {

protected static final String LABEL = "v4.0devel-PCV-with-map";
protected static final String LABEL = "removed-xsi-nil:lowered-xsi-type-resolving:improved-qname-parsing:get-children:has-children";

protected static final Trace LOGGER = TraceManager.getTrace(AbstractSchemaPerformanceTest.class);

Expand Down
87 changes: 48 additions & 39 deletions infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java
Expand Up @@ -419,19 +419,25 @@ public static List<Element> getChildElements(Element element, QName elementName)
@NotNull
public static List<Element> listChildElements(Node node) {
List<Element> subelements = new ArrayList<>();
NodeList childNodes = node.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node childNode = childNodes.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
subelements.add((Element) childNode);
Node child = node.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
subelements.add((Element) child);
}
child = child.getNextSibling();
}
return subelements;
}

public static boolean hasChildElements(Node node) {
List<Element> childElements = listChildElements(node);
return (!childElements.isEmpty());
Node child = node.getFirstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
return true;
}
child = child.getNextSibling();
}
return false;
}

public static QName resolveQName(Element element) {
Expand Down Expand Up @@ -460,29 +466,31 @@ private interface NamespaceResolver {
}

public static QName resolveQName(NamespaceResolver namespaceResolver, String qnameStringRepresentation) {
if (StringUtils.isBlank(qnameStringRepresentation)) {
// No QName
if (qnameStringRepresentation == null) {
return null;
}
String[] qnameArray = qnameStringRepresentation.split(":");
if (qnameArray.length > 2) {
throw new IllegalArgumentException("Unsupported format: more than one colon in Qname: "
+ qnameStringRepresentation);
}
QName qname;
if (qnameArray.length == 1 || qnameArray[1] == null || qnameArray[1].isEmpty()) {
// no prefix => no namespace
qname = new QName(null, qnameArray[0]);
int colonIndex = qnameStringRepresentation.indexOf(':');
if (colonIndex < 0) {
if (StringUtils.isBlank(qnameStringRepresentation)) {
// No QName
return null;
} else {
// no prefix => no namespace
return new QName(null, qnameStringRepresentation);
}
} else {
String namespacePrefix = qnameArray[0];
String namespace = namespaceResolver.resolve(namespacePrefix);
if (namespace == null) {
QNameUtil.reportUndeclaredNamespacePrefix(namespacePrefix, qnameStringRepresentation);
namespacePrefix = QNameUtil.markPrefixAsUndeclared(namespacePrefix);
String providedNamespacePrefix = qnameStringRepresentation.substring(0, colonIndex);
String localPart = qnameStringRepresentation.substring(colonIndex+1);
String namespace = namespaceResolver.resolve(providedNamespacePrefix);
String namespacePrefix;
if (namespace != null) {
namespacePrefix = providedNamespacePrefix;
} else {
QNameUtil.reportUndeclaredNamespacePrefix(providedNamespacePrefix, qnameStringRepresentation);
namespacePrefix = QNameUtil.markPrefixAsUndeclared(providedNamespacePrefix);
}
qname = new QName(namespace, qnameArray[1], namespacePrefix);
return new QName(namespace, localPart, namespacePrefix);
}
return qname;
}

private static String findNamespace(Node domNode, String prefix) {
Expand Down Expand Up @@ -964,17 +972,22 @@ public static QName getQName(Element element) {
}

public static QName getQName(Node node) {
if (node.getLocalName() == null) {
if (node.getNodeName() == null) {
String localName = node.getLocalName();
if (localName == null) {
String nodeName = node.getNodeName();
if (nodeName == null) {
return null;
} else {
return new QName(null, node.getNodeName());
return new QName(null, nodeName);
}
} else {
String prefix = node.getPrefix();
if (prefix == null) {
return new QName(node.getNamespaceURI(), localName);
} else {
return new QName(node.getNamespaceURI(), localName, prefix);
}
}
if (node.getPrefix() == null) {
return new QName(node.getNamespaceURI(), node.getLocalName());
}
return new QName(node.getNamespaceURI(), node.getLocalName(), node.getPrefix());
}

public static QName getQNameValue(Element element) {
Expand Down Expand Up @@ -1322,7 +1335,7 @@ public static Element findElementRecursive(Element element, QName elementQName)

public static QName getQNameWithoutPrefix(Node node) {
QName qname = getQName(node);
return new QName(qname.getNamespaceURI(), qname.getLocalPart());
return qname != null ? new QName(qname.getNamespaceURI(), qname.getLocalPart()) : null;
}

public static boolean isElementName(Element element, QName name) {
Expand All @@ -1334,12 +1347,8 @@ public static boolean isNil(Element element) {
return nilString != null && Boolean.parseBoolean(nilString);
}

public static void setNil(Element element) {
element.setAttributeNS(XSI_NIL.getNamespaceURI(), XSI_NIL.getLocalPart(), "true");
}

/**
* Serializes the content of the element to a string (without the eclosing element tags).
* Serializes the content of the element to a string (without the enclosing element tags).
*/
public static String serializeElementContent(Element element) {
String completeXml = serializeDOMToString(element);
Expand All @@ -1349,7 +1358,7 @@ public static String serializeElementContent(Element element) {

public static boolean isEmpty(Element element) {
return element == null ||
!hasChildElements(element) && (isNil(element) || StringUtils.isBlank(element.getTextContent()));
!hasChildElements(element) && StringUtils.isBlank(element.getTextContent());
}

public static boolean isEmpty(String textContent, Map<String, String> namespaces) {
Expand Down

0 comments on commit 797415f

Please sign in to comment.