*/
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();
+ }
}