Do not require namespace repairing
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / RandomPrefix.java
index 0251678389a8c29cd3d989fc6e9d0a1a227ba78a..125ac23eb86fd057551e821e76fa757b0f2a36bf 100644 (file)
@@ -7,43 +7,86 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
 import java.net.URI;
-import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.ThreadLocalRandom;
 
-import org.opendaylight.yangtools.yang.common.QName;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
 
-final class RandomPrefix {
-    final Map<URI, String> prefixes = new HashMap<>();
+class RandomPrefix {
+    // 32 characters, carefully chosen
+    private static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
+    private static final int MASK = 0x1f;
+    private static final int SHIFT = 5;
 
-    Iterable<Entry<URI, String>> getPrefixes() {
-        return prefixes.entrySet();
+    private int counter = 0;
+
+    // BiMap to make values lookup faster
+    private final BiMap<URI, String> prefixes = HashBiMap.create();
+    private final NamespaceContext context;
+
+    RandomPrefix() {
+        this.context = null;
     }
 
-    String encodeQName(final QName qname) {
-        return encodePrefix(qname) + ':' + qname.getLocalName();
+    RandomPrefix(final NamespaceContext context) {
+        this.context = Preconditions.checkNotNull(context);
     }
 
-    String encodePrefix(final QName qname) {
-        String prefix = prefixes.get(qname.getNamespace());
-        if (prefix == null) {
-            prefix = qname.getPrefix();
-            if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
-                final ThreadLocalRandom random = ThreadLocalRandom.current();
-                do {
-                    final StringBuilder sb = new StringBuilder();
-                    for (int i = 0; i < 4; i++) {
-                        sb.append((char)('a' + random.nextInt(25)));
-                    }
-
-                    prefix = sb.toString();
-                } while (prefixes.containsValue(prefix));
-            }
-
-            prefixes.put(qname.getNamespace(), prefix);
+    Iterable<Map.Entry<URI, String>> getPrefixes() {
+        return prefixes.entrySet();
+    }
+
+    String encodePrefix(final URI namespace) {
+        String prefix = prefixes.get(namespace);
+        if (prefix != null) {
+            return prefix;
         }
+
+        do {
+            prefix = encode(counter);
+            counter++;
+        } while (alreadyUsedPrefix(prefix));
+
+        prefixes.put(namespace, prefix);
         return prefix;
     }
+
+    private boolean alreadyUsedPrefix(final String prefix) {
+        if (context == null) {
+            return false;
+        }
+
+        final String str = context.getNamespaceURI(prefix);
+        return !XMLConstants.NULL_NS_URI.equals(str);
+    }
+
+    @VisibleForTesting
+    static int decode(final String str) {
+        int ret = 0;
+        for (char c : str.toCharArray()) {
+            int idx = LOOKUP.indexOf(c);
+            Preconditions.checkArgument(idx != -1, "Invalid string %s", str);
+            ret = (ret << SHIFT) + idx;
+        }
+
+        return ret;
+    }
+
+    @VisibleForTesting
+    static String encode(int num) {
+        final StringBuilder sb = new StringBuilder();
+
+        do {
+            sb.append(LOOKUP.charAt(num & MASK));
+            num >>>= SHIFT;
+        } while (num != 0);
+
+        return sb.reverse().toString();
+    }
 }