BUG-1275: optimize InstanceIdentifierForXmlCodec 41/8541/3
authorRobert Varga <rovarga@cisco.com>
Wed, 2 Jul 2014 08:38:21 +0000 (10:38 +0200)
committerRobert Varga <rovarga@cisco.com>
Wed, 2 Jul 2014 16:17:01 +0000 (18:17 +0200)
First batch of optimizations:
- use Splitter instead of String.split()
- use characters instead of Strings
- use ThreadLocalRandom
- sue SchemaContext.findModuleByNamespace()

Various optimizations revolving around precompiling Patterns, using
characters instead of strings,

Change-Id: Id2befcc49d4346917a39d434b005b07fe3f50b7c
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/InstanceIdentifierForXmlCodec.java

index 04b49a7fde19e8b3b9be435240f40991e41bef76..130299cd20abb502137ae2513c1e29bd4581f62d 100644 (file)
@@ -1,13 +1,24 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
 package org.opendaylight.yangtools.yang.data.impl.codec;
 
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -20,103 +31,98 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.w3c.dom.Element;
 
-import com.google.common.base.Preconditions;
-
 public class InstanceIdentifierForXmlCodec {
-
     private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
-    public static final String SQUOTE = "'";
-    public static final String DQUOTE = "\"";
+    private static final Splitter SLASH_SPLITTER = Splitter.on('/');
+    private static final Splitter COLON_SPLITTER = Splitter.on(':');
+    private static final String SQUOTE = "'";
+    private static final String DQUOTE = "\"";
 
     public static final InstanceIdentifierForXmlCodec INSTANCE_IDENTIFIER_FOR_XML_CODEC = new InstanceIdentifierForXmlCodec();
 
-    public InstanceIdentifierForXmlCodec() {
+    protected InstanceIdentifierForXmlCodec() {
     }
 
-    public InstanceIdentifier deserialize(Element element, SchemaContext schemaContext) {
+    public InstanceIdentifier deserialize(final Element element, final SchemaContext schemaContext) {
         Preconditions.checkNotNull(element, "Value of element for deserialization can't be null");
         Preconditions.checkNotNull(schemaContext,
                 "Schema context for deserialization of instance identifier type can't be null");
 
-        String valueTrimmed = element.getTextContent().trim();
-        if (!valueTrimmed.startsWith("/")) {
-            return null;
-        }
-        String[] xPathParts = valueTrimmed.split("/");
-        if (xPathParts.length < 2) { // must be at least "/pr:node"
+        final String valueTrimmed = element.getTextContent().trim();
+        final Iterator<String> xPathParts = SLASH_SPLITTER.split(valueTrimmed).iterator();
+
+        // must be at least "/pr:node"
+        if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) {
             return null;
         }
+
         List<PathArgument> result = new ArrayList<>();
-        for (int i = 1; i < xPathParts.length; i++) {
-            String xPathPartTrimmed = xPathParts[i].trim();
+        while (xPathParts.hasNext()) {
+            String xPathPartTrimmed = xPathParts.next().trim();
 
             PathArgument pathArgument = toPathArgument(xPathPartTrimmed, element, schemaContext);
             if (pathArgument != null) {
                 result.add(pathArgument);
             }
         }
-        return new InstanceIdentifier(result);
+        return InstanceIdentifier.create(result);
     }
 
-    public Element serialize(InstanceIdentifier data, Element element) {
+    public Element serialize(final InstanceIdentifier data, final Element element) {
         Preconditions.checkNotNull(data, "Variable should contain instance of instance identifier and can't be null");
         Preconditions.checkNotNull(element, "DOM element can't be null");
         Map<String, String> prefixes = new HashMap<>();
         StringBuilder textContent = new StringBuilder();
-        for (PathArgument pathArgument : data.getPath()) {
-            textContent.append("/");
+        for (PathArgument pathArgument : data.getPathArguments()) {
+            textContent.append('/');
             writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
             if (pathArgument instanceof NodeIdentifierWithPredicates) {
                 Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
 
                 for (QName keyValue : predicates.keySet()) {
                     String predicateValue = String.valueOf(predicates.get(keyValue));
-                    textContent.append("[");
+                    textContent.append('[');
                     writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
                     textContent.append("='");
                     textContent.append(predicateValue);
-                    textContent.append("'");
-                    textContent.append("]");
+                    textContent.append("']");
                 }
             } else if (pathArgument instanceof NodeWithValue) {
                 textContent.append("[.='");
                 textContent.append(((NodeWithValue) pathArgument).getValue());
-                textContent.append("'");
-                textContent.append("]");
+                textContent.append("']");
             }
         }
         element.setTextContent(textContent.toString());
         return element;
     }
 
-    private String getIdAndPrefixAsStr(String pathPart) {
-        int predicateStartIndex = pathPart.indexOf("[");
+    private static String getIdAndPrefixAsStr(final String pathPart) {
+        int predicateStartIndex = pathPart.indexOf('[');
         return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
     }
 
-    private PathArgument toPathArgument(String xPathArgument, Element element, SchemaContext schemaContext) {
-
-        QName mainQName = toIdentity(xPathArgument, element, schemaContext);
+    private PathArgument toPathArgument(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+        final QName mainQName = toIdentity(xPathArgument, element, schemaContext);
 
         // predicates
+        final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
+        final Map<QName, Object> predicates = new HashMap<>();
         QName currentQName = mainQName;
-        List<String> predicatesStr = new ArrayList<>();
-        Map<QName, Object> predicates = new HashMap<>();
-        Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument);
+
         while (matcher.find()) {
-            predicatesStr.add(matcher.group(1).trim());
-        }
-        for (String predicateStr : predicatesStr) {
-            int indexOfEqualityMark = predicateStr.indexOf("=");
+            final String predicateStr = matcher.group(1).trim();
+            final int indexOfEqualityMark = predicateStr.indexOf('=');
             if (indexOfEqualityMark != -1) {
-                String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
-                if (predicateStr.startsWith(".")) { // it is leaf-list
-                    if (predicateValue == null) {
-                        return null;
-                    }
-                } else {
+                final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1));
+                if (predicateValue == null) {
+                    return null;
+                }
+
+                if (predicateStr.charAt(0) != '.') {
+                    // target is not a leaf-list
                     currentQName = toIdentity(predicateStr.substring(0, indexOfEqualityMark), element, schemaContext);
-                    if (currentQName == null || predicateValue == null) {
+                    if (currentQName == null) {
                         return null;
                     }
                 }
@@ -132,22 +138,30 @@ public class InstanceIdentifierForXmlCodec {
 
     }
 
-    public QName toIdentity(String xPathArgument, Element element, SchemaContext schemaContext) {
-        String xPathPart = getIdAndPrefixAsStr(xPathArgument);
-        String xPathPartTrimmed = xPathPart.trim();
-        if (xPathPartTrimmed.isEmpty()) {
+    public QName toIdentity(final String xPathArgument, final Element element, final SchemaContext schemaContext) {
+        final String xPathPartTrimmed = getIdAndPrefixAsStr(xPathArgument).trim();
+        final Iterator<String> it = COLON_SPLITTER.split(xPathPartTrimmed).iterator();
+
+        // Empty string
+        if (!it.hasNext()) {
             return null;
         }
-        String[] prefixAndIdentifier = xPathPartTrimmed.split(":");
+
+        final String prefix = it.next().trim();
+        if (prefix.isEmpty()) {
+            return null;
+        }
+
         // it is not "prefix:value"
-        if (prefixAndIdentifier.length != 2) {
+        if (!it.hasNext()) {
             return null;
         }
-        String prefix = prefixAndIdentifier[0].trim();
-        String identifier = prefixAndIdentifier[1].trim();
-        if (prefix.isEmpty() || identifier.isEmpty()) {
+
+        final String identifier = it.next().trim();
+        if (identifier.isEmpty()) {
             return null;
         }
+
         URI namespace = null;
         String namespaceStr = null;
         try {
@@ -158,28 +172,27 @@ public class InstanceIdentifierForXmlCodec {
         } catch (NullPointerException e) {
             throw new IllegalArgumentException("I wasn't possible to get namespace for prefix " + prefix);
         }
-        Module youngestModule = findYoungestModuleByNamespace(schemaContext, namespace);
 
+        Module youngestModule = findYoungestModuleByNamespace(schemaContext, namespace);
         return QName.create(namespace, youngestModule.getRevision(), identifier);
     }
 
-    private Module findYoungestModuleByNamespace(SchemaContext schemaContext, URI namespace) {
+    // FIXME: this method should be provided by SchemaContext
+    private static Module findYoungestModuleByNamespace(final SchemaContext schemaContext, final URI namespace) {
         Module result = null;
-        for (Module module : schemaContext.getModules()) {
-            if (namespace.equals(module.getNamespace())) {
-                if (result != null) {
-                    if (module.getRevision().after(result.getRevision())) {
-                        result = module;
-                    }
-                } else {
+        for (Module module : schemaContext.findModuleByNamespace(namespace)) {
+            if (result != null) {
+                if (module.getRevision().after(result.getRevision())) {
                     result = module;
                 }
+            } else {
+                result = module;
             }
         }
         return result;
     }
 
-    private static String toPredicateValue(String predicatedValue) {
+    private static String toPredicateValue(final String predicatedValue) {
         String predicatedValueTrimmed = predicatedValue.trim();
         if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
                 && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
@@ -188,8 +201,8 @@ public class InstanceIdentifierForXmlCodec {
         return null;
     }
 
-    private static void writeIdentifierWithNamespacePrefix(Element element, StringBuilder textContent, QName qName,
-            Map<String, String> prefixes) {
+    private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, final QName qName,
+            final Map<String, String> prefixes) {
         String namespace = qName.getNamespace().toString();
         String prefix = prefixes.get(namespace);
         if (prefix == null) {
@@ -203,21 +216,23 @@ public class InstanceIdentifierForXmlCodec {
         textContent.append(prefix);
         prefixes.put(namespace, prefix);
 
-        textContent.append(":");
+        textContent.append(':');
         textContent.append(qName.getLocalName());
     }
 
-    private static String generateNewPrefix(Collection<String> prefixes) {
-        StringBuilder result = null;
-        Random random = new Random();
+    private static String generateNewPrefix(final Collection<String> prefixes) {
+        String result;
+
+        final ThreadLocalRandom random = ThreadLocalRandom.current();
         do {
-            result = new StringBuilder();
+            StringBuilder sb = new StringBuilder();
             for (int i = 0; i < 4; i++) {
-                int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26);
-                result.append(Character.toChars(randomNumber));
+                sb.append('a' + random.nextInt(25));
             }
-        } while (prefixes.contains(result.toString()));
 
-        return result.toString();
+            result = sb.toString();
+        } while (prefixes.contains(result));
+
+        return result;
     }
 }