Merge "Change yang-maven-plugin to write yang files to build output"
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / codec / xml / RandomPrefix.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.data.impl.codec.xml;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Strings;
12 import com.google.common.collect.BiMap;
13 import com.google.common.collect.HashBiMap;
14 import java.net.URI;
15 import java.util.Map;
16 import org.opendaylight.yangtools.yang.common.QName;
17
18 final class RandomPrefix {
19
20     public static final char STARTING_CHAR = 'a';
21     public static final int CHARACTER_RANGE = 26;
22     public static final int PREFIX_MAX_LENGTH = 4;
23
24     public static final int MAX_COUNTER_VALUE = (int) Math.pow(CHARACTER_RANGE, PREFIX_MAX_LENGTH);
25     private static final int STARTING_WITH_XML = decode("xml");
26
27     private int counter = 0;
28
29     // BiMap to make values lookup faster
30     private final BiMap<URI, String> prefixes = HashBiMap.create();
31
32     Iterable<Map.Entry<URI, String>> getPrefixes() {
33         return prefixes.entrySet();
34     }
35
36     String encodeQName(final QName qname) {
37         return encodePrefix(qname) + ':' + qname.getLocalName();
38     }
39
40     String encodePrefix(final QName qname) {
41         String prefix = prefixes.get(qname.getNamespace());
42         if (prefix != null) {
43             return prefix;
44         }
45
46         // Reuse prefix from QName if possible
47         final String qNamePrefix = qname.getPrefix();
48
49         if (!Strings.isNullOrEmpty(qNamePrefix) && !qNamePrefix.startsWith("xml") && !alreadyUsedPrefix(qNamePrefix)) {
50             prefix = qNamePrefix;
51         } else {
52
53             do {
54                 // Skip values starting with xml (Expecting only 4 chars max since division is calculated only once)
55                 while (counter == STARTING_WITH_XML
56                         || counter / CHARACTER_RANGE == STARTING_WITH_XML) {
57                     counter++;
58                 }
59
60                 // Reset in case of max prefix generated
61                 if (counter >= MAX_COUNTER_VALUE) {
62                     counter = 0;
63                     prefixes.clear();
64                 }
65
66                 prefix = encode(counter);
67                 counter++;
68             } while (alreadyUsedPrefix(prefix));
69         }
70
71         prefixes.put(qname.getNamespace(), prefix);
72         return prefix;
73     }
74
75     private boolean alreadyUsedPrefix(final String prefix) {
76         return prefixes.values().contains(prefix);
77     }
78
79     @VisibleForTesting
80     static int decode(final String s) {
81         int num = 0;
82         for (final char ch : s.toCharArray()) {
83             num *= CHARACTER_RANGE;
84             num += (ch - STARTING_CHAR);
85         }
86         return num;
87     }
88
89     @VisibleForTesting
90     static String encode(int num) {
91         if (num == 0) {
92             return "a";
93         }
94
95         final StringBuilder sb = new StringBuilder();
96         while (num != 0) {
97             sb.append(((char) (num % CHARACTER_RANGE + STARTING_CHAR)));
98             num /= CHARACTER_RANGE;
99         }
100
101         return sb.reverse().toString();
102     }
103 }