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 com.google.common.annotations.VisibleForTesting;
11 import com.google.common.collect.BiMap;
12 import com.google.common.collect.HashBiMap;
13 import java.util.Comparator;
14 import java.util.List;
15 import java.util.Map.Entry;
16 import java.util.stream.Collectors;
17 import javax.xml.XMLConstants;
18 import javax.xml.namespace.NamespaceContext;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.opendaylight.yangtools.concepts.Mutable;
22 import org.opendaylight.yangtools.yang.common.XMLNamespace;
25 * Interface to prefix assignment based on a {@link NamespaceContext} and advice from {@link PreferredPrefixes}.
27 final class NamespacePrefixes implements Mutable {
28 // 32 characters, carefully chosen
30 static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
32 static final int SHIFT = 5;
33 private static final int MASK = 0x1f;
35 private int counter = 0;
37 // BiMap to make values lookup faster
38 private final BiMap<XMLNamespace, String> emittedPrefixes = HashBiMap.create();
39 private final PreferredPrefixes pref;
40 private final NamespaceContext context;
42 NamespacePrefixes(final NamespaceContext context, final @Nullable PreferredPrefixes pref) {
43 this.context = context;
47 List<Entry<XMLNamespace, String>> emittedPrefixes() {
48 return emittedPrefixes.entrySet().stream()
50 .sorted(Comparator.comparing(Entry::getValue))
51 .collect(Collectors.toList());
54 @NonNull String encodePrefix(final XMLNamespace namespace) {
55 var prefix = emittedPrefixes.get(namespace);
60 if (context != null) {
61 prefix = context.getPrefix(namespace.toString());
67 prefix = createPrefix(namespace);
68 emittedPrefixes.put(namespace, prefix);
72 private @NonNull String createPrefix(final @NonNull XMLNamespace namespace) {
74 final var prefix = pref.prefixForNamespace(namespace);
82 prefix = encode(counter++);
83 } while (alreadyUsedPrefix(prefix));
87 private boolean alreadyUsedPrefix(final String prefix) {
88 if (pref != null && pref.isUsed(prefix)) {
91 if (context == null) {
95 // It seems JDK8 is violating the API contract of NamespaceContext by returning null for unbound prefixes,
96 // rather than specified NULL_NS_URI. Work this around by checking explicitly for null.
97 final var str = context.getNamespaceURI(prefix);
98 return str != null && !XMLConstants.NULL_NS_URI.equals(str);
102 static @NonNull String encode(int num) {
103 final var sb = new StringBuilder();
106 sb.append(LOOKUP.charAt(num & MASK));
110 return sb.reverse().toString();