2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.data.codec.xml;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.collect.BiMap;
14 import com.google.common.collect.HashBiMap;
15 import java.util.Comparator;
16 import java.util.List;
17 import java.util.Map.Entry;
18 import java.util.stream.Collectors;
19 import javax.xml.XMLConstants;
20 import javax.xml.namespace.NamespaceContext;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.yangtools.concepts.Mutable;
23 import org.opendaylight.yangtools.yang.common.XMLNamespace;
26 * Interface to prefix assignment based on a {@link NamespaceContext} and advice from {@link PreferredPrefixes}.
28 final class NamespacePrefixes implements Mutable {
29 // 32 characters, carefully chosen
31 static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
33 static final int SHIFT = 5;
34 private static final int MASK = 0x1f;
36 private int counter = 0;
38 // BiMap to make values lookup faster
39 private final BiMap<XMLNamespace, String> emittedPrefixes = HashBiMap.create();
40 private final PreferredPrefixes pref;
41 private final NamespaceContext context;
43 NamespacePrefixes(final PreferredPrefixes pref, final NamespaceContext context) {
44 this.pref = requireNonNull(pref);
45 this.context = context;
48 List<Entry<XMLNamespace, String>> emittedPrefixes() {
49 return emittedPrefixes.entrySet().stream()
51 .sorted(Comparator.comparing(Entry::getValue))
52 .collect(Collectors.toList());
55 @NonNull String encodePrefix(final XMLNamespace namespace) {
56 var prefix = emittedPrefixes.get(namespace);
61 if (context != null) {
62 prefix = context.getPrefix(namespace.toString());
68 prefix = pref.prefixForNamespace(namespace);
70 prefix = encode(counter++);
73 while (alreadyUsedPrefix(prefix)) {
74 prefix = encode(counter++);
77 emittedPrefixes.put(namespace, prefix);
81 private boolean alreadyUsedPrefix(final String prefix) {
82 if (context == null) {
86 // It seems JDK8 is violating the API contract of NamespaceContext by returning null for unbound prefixes,
87 // rather than specified NULL_NS_URI. Work this around by checking explicitly for null.
88 final var str = context.getNamespaceURI(prefix);
89 return str != null && !XMLConstants.NULL_NS_URI.equals(str);
93 static @NonNull String encode(int num) {
94 final var sb = new StringBuilder();
97 sb.append(LOOKUP.charAt(num & MASK));
101 return sb.reverse().toString();