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.lazy(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 @SuppressWarnings("PMD.ShortMethodName")
94 @NonNull
95 public IEnhancedQName cachedQNameFor(@NonNull String namespace, @NonNull String name) {
96 int namespacePosition = namespaceCache.indexOf(namespace);
97
98 Map<String, QNameRecord> namespaceNames = nsIndexToLocalNameToIndex
99 .computeIfAbsent(namespacePosition, key -> new ConcurrentHashMap<>());
100
101 return ObjectUtils.notNull(namespaceNames.computeIfAbsent(name, key -> {
102 assert key != null;
103 QNameRecord record = new QNameRecord(namespacePosition, namespace, key);
104 indexToQName.put(record.getIndexPosition(), record);
105 return record;
106 }));
107 }
108
109
110
111
112
113
114
115
116
117
118
119
120 @NonNull
121 Optional<IEnhancedQName> get(@NonNull String namespace, @NonNull String name) {
122 Optional<Integer> nsPosition = namespaceCache.get(namespace);
123 if (!nsPosition.isPresent()) {
124 throw new IllegalArgumentException(
125 String.format("The namespace '%s' is not recognized.", namespace));
126 }
127
128 Map<String, QNameRecord> namespaceNames = nsIndexToLocalNameToIndex.get(nsPosition.get());
129 return ObjectUtils.notNull(Optional.ofNullable(
130 namespaceNames == null
131 ? null
132 : namespaceNames.get(name)));
133 }
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 @Nullable
149 public IEnhancedQName get(int index) {
150 return indexToQName.get(index);
151 }
152
153 private final class QNameRecord implements IEnhancedQName {
154 private final int qnameIndexPosition;
155 private final int namespaceIndexPosition;
156 @NonNull
157 private final String namespace;
158 @NonNull
159 private final String localName;
160
161 public QNameRecord(
162 int namespaceIndexPosition,
163 @NonNull String namespace,
164 @NonNull String localName) {
165 this.qnameIndexPosition = indexCounter.getAndIncrement();
166 this.namespaceIndexPosition = namespaceIndexPosition;
167 this.namespace = namespace;
168 this.localName = localName;
169 }
170
171 @Override
172 public int getIndexPosition() {
173 return qnameIndexPosition;
174 }
175
176 @Override
177 public URI getNamespaceAsUri() {
178 return ObjectUtils.notNull(getNamespaceCache().getAsURI(namespaceIndexPosition).get());
179 }
180
181 @Override
182 public String getNamespace() {
183 return namespace;
184 }
185
186 @Override
187 public String getLocalName() {
188 return localName;
189 }
190
191 @Override
192 public int hashCode() {
193 return Objects.hashCode(qnameIndexPosition);
194 }
195
196 @SuppressWarnings("PMD.OnlyOneReturn")
197 @Override
198 public boolean equals(Object obj) {
199 if (this == obj) {
200 return true;
201 }
202 if (obj == null || getClass() != obj.getClass()) {
203 return false;
204 }
205 QNameRecord other = (QNameRecord) obj;
206 return Objects.equals(qnameIndexPosition, other.getIndexPosition());
207 }
208
209 @Override
210 public int compareTo(IEnhancedQName other) {
211 return COMPARATOR.compare(this, other);
212 }
213
214 @Override
215 public String toString() {
216 return toEQName();
217 }
218 }
219
220 }