package org.opendaylight.yangtools.yang.data.impl.codec.xml;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
+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.Map;
-import org.opendaylight.yangtools.yang.common.QName;
-
-final class RandomPrefix {
- public static final char STARTING_CHAR = 'a';
- public static final int CHARACTER_RANGE = 26;
- public static final int PREFIX_MAX_LENGTH = 4;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
- public static final int MAX_COUNTER_VALUE = (int) Math.pow(CHARACTER_RANGE, PREFIX_MAX_LENGTH);
- private static final int STARTING_WITH_XML = decode("xml");
+class RandomPrefix {
+ // 32 characters, carefully chosen
+ private static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
+ private static final int MASK = 0x1f;
+ private static final int SHIFT = 5;
private int counter = 0;
// BiMap to make values lookup faster
private final BiMap<URI, String> prefixes = HashBiMap.create();
+ private final NamespaceContext context;
- Iterable<Map.Entry<URI, String>> getPrefixes() {
- return prefixes.entrySet();
+ RandomPrefix() {
+ this.context = null;
}
- String encodeQName(final QName qname) {
- return encodePrefix(qname) + ':' + qname.getLocalName();
+ RandomPrefix(final NamespaceContext context) {
+ this.context = Preconditions.checkNotNull(context);
+ }
+
+ Iterable<Map.Entry<URI, String>> getPrefixes() {
+ return prefixes.entrySet();
}
- String encodePrefix(final QName qname) {
- String prefix = prefixes.get(qname.getNamespace());
+ String encodePrefix(final URI namespace) {
+ String prefix = prefixes.get(namespace);
if (prefix != null) {
return prefix;
}
- // Reuse prefix from QName if possible
- final String qNamePrefix = qname.getPrefix();
-
- if (!Strings.isNullOrEmpty(qNamePrefix) && !qNamePrefix.startsWith("xml") && !alreadyUsedPrefix(qNamePrefix)) {
- prefix = qNamePrefix;
- } else {
-
- do {
- // Skip values starting with xml (Expecting only 4 chars max since division is calculated only once)
- while (counter == STARTING_WITH_XML
- || counter / CHARACTER_RANGE == STARTING_WITH_XML) {
- counter++;
- }
-
- // Reset in case of max prefix generated
- if (counter >= MAX_COUNTER_VALUE) {
- counter = 0;
- prefixes.clear();
- }
-
- prefix = encode(counter);
- counter++;
- } while (alreadyUsedPrefix(prefix));
- }
+ do {
+ prefix = encode(counter);
+ counter++;
+ } while (alreadyUsedPrefix(prefix));
- prefixes.put(qname.getNamespace(), prefix);
+ prefixes.put(namespace, prefix);
return prefix;
}
private boolean alreadyUsedPrefix(final String prefix) {
- return prefixes.values().contains(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 s) {
- int num = 0;
- for (final char ch : s.toCharArray()) {
- num *= CHARACTER_RANGE;
- num += (ch - STARTING_CHAR);
+ 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 num;
+
+ return ret;
}
@VisibleForTesting
static String encode(int num) {
- if (num == 0) {
- return "a";
- }
-
final StringBuilder sb = new StringBuilder();
- while (num != 0) {
- sb.append(((char) (num % CHARACTER_RANGE + STARTING_CHAR)));
- num /= CHARACTER_RANGE;
- }
+
+ do {
+ sb.append(LOOKUP.charAt(num & MASK));
+ num >>>= SHIFT;
+ } while (num != 0);
return sb.reverse().toString();
}