1
2
3
4
5
6 package gov.nist.secauto.metaschema.core.qname;
7
8 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
9
10 import java.net.URI;
11 import java.util.Comparator;
12 import java.util.Map;
13 import java.util.Objects;
14 import java.util.Optional;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.atomic.AtomicInteger;
17
18 import edu.umd.cs.findbugs.annotations.NonNull;
19 import edu.umd.cs.findbugs.annotations.Nullable;
20 import nl.talsmasoftware.lazy4j.Lazy;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public final class QNameCache {
39
40 private static final Comparator<IEnhancedQName> COMPARATOR
41 = Comparator.comparingInt(IEnhancedQName::getIndexPosition);
42
43 @NonNull
44 private static final Lazy<QNameCache> INSTANCE = ObjectUtils.notNull(Lazy.of(QNameCache::new));
45
46 @NonNull
47 private final NamespaceCache namespaceCache;
48
49 private final Map<Integer, QNameRecord> indexToQName = new ConcurrentHashMap<>();
50 private final Map<Integer, Map<String, QNameRecord>> nsIndexToLocalNameToIndex = new ConcurrentHashMap<>();
51
52
53
54
55 private final AtomicInteger indexCounter = new AtomicInteger();
56
57
58
59
60
61
62 @NonNull
63 public static QNameCache instance() {
64 return ObjectUtils.notNull(INSTANCE.get());
65 }
66
67 private QNameCache() {
68
69 this(NamespaceCache.instance());
70 }
71
72 private QNameCache(@NonNull NamespaceCache nsCache) {
73 this.namespaceCache = nsCache;
74 }
75
76 @NonNull
77 private NamespaceCache getNamespaceCache() {
78 return namespaceCache;
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93 @NonNull
94 public IEnhancedQName cachedQNameFor(@NonNull String namespace, @NonNull String name) {
95 int namespacePosition = namespaceCache.indexOf(namespace);
96
97 Map<String, QNameRecord> namespaceNames = nsIndexToLocalNameToIndex
98 .computeIfAbsent(namespacePosition, key -> new ConcurrentHashMap<>());
99
100 return ObjectUtils.notNull(namespaceNames.computeIfAbsent(name, key -> {
101 assert key != null;
102 QNameRecord record = new QNameRecord(namespacePosition, namespace, key);
103 indexToQName.put(record.getIndexPosition(), record);
104 return record;
105 }));
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119 @NonNull
120 Optional<IEnhancedQName> get(@NonNull String namespace, @NonNull String name) {
121 Optional<Integer> nsPosition = namespaceCache.get(namespace);
122 if (!nsPosition.isPresent()) {
123 throw new IllegalArgumentException(
124 String.format("The namespace '%s' is not recognized.", namespace));
125 }
126
127 Map<String, QNameRecord> namespaceNames = nsIndexToLocalNameToIndex.get(nsPosition.get());
128 return ObjectUtils.notNull(Optional.ofNullable(
129 namespaceNames == null
130 ? null
131 : namespaceNames.get(name)));
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 @Nullable
148 public IEnhancedQName get(int index) {
149 return indexToQName.get(index);
150 }
151
152 private final class QNameRecord implements IEnhancedQName {
153 private final int qnameIndexPosition;
154 private final int namespaceIndexPosition;
155 @NonNull
156 private final String namespace;
157 @NonNull
158 private final String localName;
159
160 public QNameRecord(
161 int namespaceIndexPosition,
162 @NonNull String namespace,
163 @NonNull String localName) {
164 this.qnameIndexPosition = indexCounter.getAndIncrement();
165 this.namespaceIndexPosition = namespaceIndexPosition;
166 this.namespace = namespace;
167 this.localName = localName;
168 }
169
170 @Override
171 public int getIndexPosition() {
172 return qnameIndexPosition;
173 }
174
175 @Override
176 public URI getNamespaceAsUri() {
177 return ObjectUtils.notNull(getNamespaceCache().getAsURI(namespaceIndexPosition).get());
178 }
179
180 @Override
181 public String getNamespace() {
182 return namespace;
183 }
184
185 @Override
186 public String getLocalName() {
187 return localName;
188 }
189
190 @Override
191 public int hashCode() {
192 return Objects.hashCode(qnameIndexPosition);
193 }
194
195 @Override
196 public boolean equals(Object obj) {
197 if (this == obj) {
198 return true;
199 }
200 if (obj == null || getClass() != obj.getClass()) {
201 return false;
202 }
203 QNameRecord other = (QNameRecord) obj;
204 return Objects.equals(qnameIndexPosition, other.getIndexPosition());
205 }
206
207 @Override
208 public int compareTo(IEnhancedQName other) {
209 return COMPARATOR.compare(this, other);
210 }
211
212 @Override
213 public String toString() {
214 return toEQName();
215 }
216 }
217
218 }