1
1
/*
2
+ * Elemental
3
+ * Copyright (C) 2024, Evolved Binary Ltd
4
+ *
5
+
6
+ * https://www.evolvedbinary.com | https://www.elemental.xyz
7
+ *
8
+ * This library is free software; you can redistribute it and/or
9
+ * modify it under the terms of the GNU Lesser General Public
10
+ * License as published by the Free Software Foundation; version 2.1.
11
+ *
12
+ * This library is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ * Lesser General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU Lesser General Public
18
+ * License along with this library; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
+ *
21
+ * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22
+ * The original license header is included below.
23
+ *
24
+ * =====================================================================
25
+ *
2
26
* eXist-db Open Source Native XML Database
3
27
* Copyright (C) 2001 The eXist-db Authors
4
28
*
26
50
import org .exist .util .XMLNames ;
27
51
import org .exist .xquery .Constants ;
28
52
53
+ import javax .annotation .Nullable ;
29
54
import javax .xml .XMLConstants ;
30
55
import java .util .regex .Matcher ;
31
56
import java .util .regex .Pattern ;
32
57
33
58
import static org .exist .dom .QName .Validity .*;
59
+ import static org .exist .util .StringUtil .isNullOrEmpty ;
34
60
35
61
/**
36
62
* Represents a QName, consisting of a local name, a namespace URI and a prefix.
37
63
*
38
64
* @author <a href="mailto:[email protected] ">Wolfgang</a>
65
+ * @author <a href="mailto:[email protected] ">Adam Retter</a>
39
66
*/
40
67
public class QName implements Comparable <QName > {
41
68
@@ -134,7 +161,18 @@ public byte getNameType() {
134
161
* @return the string representation of this qualified name.
135
162
* */
136
163
public String getStringValue () {
137
- return getStringRepresentation (false );
164
+ return getStringRepresentation (false , false );
165
+ }
166
+
167
+ /**
168
+ * Get an extended string representation of this qualified name.
169
+ *
170
+ * Will be of the format `local-name`, `{namespace}local-name`, or `{namespace}prefix:local-name`.
171
+ *
172
+ * @return the string representation of this qualified name.
173
+ */
174
+ public String getExtendedStringValue () {
175
+ return getStringRepresentation (false , true );
138
176
}
139
177
140
178
/**
@@ -146,23 +184,32 @@ public String getStringValue() {
146
184
*/
147
185
@ Override
148
186
public String toString () {
149
- return getStringRepresentation (true );
187
+ return getStringRepresentation (true , false );
150
188
}
151
189
152
190
/**
153
191
* Get a string representation of this qualified name.
154
192
*
155
193
* @param showNsWithoutPrefix true if the namespace should be shown even when there is no prefix, false otherwise.
156
- * When shown, it will be output using Clark notation, e.g. `{http://namespace}local-name`.
194
+ * When shown, it will be output using Clark notation, e.g. `{namespace}local-name`.
195
+ *
196
+ * @param extended true if the namespace and prefix should be shown, requires showNsWithoutPrefix == false.
157
197
*
158
198
* @return the string representation of this qualified name.
159
199
*/
160
- private String getStringRepresentation (final boolean showNsWithoutPrefix ) {
200
+ private String getStringRepresentation (final boolean showNsWithoutPrefix , final boolean extended ) {
161
201
if (prefix != null && !prefix .isEmpty ()) {
162
- return prefix + COLON + localPart ;
163
- } else if (showNsWithoutPrefix && namespaceURI != null && !XMLConstants .NULL_NS_URI .equals (namespaceURI )) {
202
+ if (extended ) {
203
+ return LEFT_BRACE + namespaceURI + RIGHT_BRACE + prefix + COLON + localPart ;
204
+ } else {
205
+ return prefix + COLON + localPart ;
206
+ }
207
+ }
208
+
209
+ if (showNsWithoutPrefix && namespaceURI != null && !XMLConstants .NULL_NS_URI .equals (namespaceURI )) {
164
210
return LEFT_BRACE + namespaceURI + RIGHT_BRACE + localPart ;
165
211
}
212
+
166
213
return localPart ;
167
214
}
168
215
@@ -330,6 +377,52 @@ public static QName parse(final String namespaceURI, final String qname) throws
330
377
return new QName (qname .substring (p + 1 ), namespaceURI , qname .substring (0 , p ));
331
378
}
332
379
380
+ /**
381
+ * Extract a QName from a namespace and qualified name string.
382
+ *
383
+ * @param extendedStringValue a string representation as produced by {@link #getExtendedStringValue()}, i.e.: `local-name`, `{namespace}local-name`, or `{namespace}prefix:local-name`.
384
+ * @return The QName
385
+ * @throws IllegalQNameException if the qname component is invalid
386
+ */
387
+ public static QName parse (String extendedStringValue ) throws IllegalQNameException {
388
+ if (isNullOrEmpty (extendedStringValue )) {
389
+ throw new IllegalQNameException (ILLEGAL_FORMAT .val , "Illegal extended string QName is empty" );
390
+ }
391
+
392
+ final String namespaceUri ;
393
+ if (extendedStringValue .charAt (0 ) == LEFT_BRACE ) {
394
+ final int idxNsEnd = extendedStringValue .indexOf (RIGHT_BRACE );
395
+ if (idxNsEnd == Constants .STRING_NOT_FOUND ) {
396
+ throw new IllegalQNameException (ILLEGAL_FORMAT .val , "Illegal extended string QName, missing right brace: '" + extendedStringValue + "'" );
397
+ }
398
+ namespaceUri = extendedStringValue .substring (1 , idxNsEnd );
399
+ extendedStringValue = extendedStringValue .substring (idxNsEnd + 1 );
400
+ } else if (extendedStringValue .indexOf (RIGHT_BRACE ) != Constants .STRING_NOT_FOUND ) {
401
+ throw new IllegalQNameException (ILLEGAL_FORMAT .val , "Illegal extended string QName, missing left brace: '" + extendedStringValue + "'" );
402
+ } else {
403
+ namespaceUri = XMLConstants .NULL_NS_URI ;
404
+ }
405
+
406
+ @ Nullable final String prefix ;
407
+ final int idxColon = extendedStringValue .indexOf (COLON );
408
+ if (idxColon == Constants .STRING_NOT_FOUND ) {
409
+ prefix = null ;
410
+ } else {
411
+ prefix = extendedStringValue .substring (0 , idxColon );
412
+ if (!XMLNames .isNCName (prefix )) {
413
+ throw new IllegalQNameException (INVALID_PREFIX .val , "Illegal extended string QName, invalid prefix: '" + extendedStringValue + "'" );
414
+ }
415
+ extendedStringValue = extendedStringValue .substring (idxColon + 1 );
416
+ }
417
+
418
+ final String localPart = extendedStringValue ;
419
+ if (!XMLNames .isNCName (localPart )) {
420
+ throw new IllegalQNameException (INVALID_LOCAL_PART .val , "Illegal extended string QName, invalid prefix: '" + extendedStringValue + "'" );
421
+ }
422
+
423
+ return new QName (localPart , namespaceUri , prefix );
424
+ }
425
+
333
426
/**
334
427
* Parses the given string into a QName. The method uses context to look up
335
428
* a namespace URI for an existing prefix.
0 commit comments