Skip to content

Commit 2a0eded

Browse files
add support for ##any default element namespace (#2629)
1 parent fb364f8 commit 2a0eded

File tree

4 files changed

+59
-12
lines changed

4 files changed

+59
-12
lines changed

basex-core/src/main/java/org/basex/query/QueryParser.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ private boolean defaultNamespaceDecl(final boolean fixed) throws QueryException
539539
final boolean elem = wsConsumeWs(ELEMENT);
540540
if(!elem && !wsConsumeWs(FUNCTION)) return false;
541541
wsCheck(NAMESPACE);
542-
final byte[] uri = uriLiteral();
542+
final byte[] uri = elem ? uriLiteral(ANY_URI) : uriLiteral();
543543
if(eq(XML_URI, uri)) throw error(BINDXMLURI_X_X, uri, XML);
544544
if(eq(XMLNS_URI, uri)) throw error(BINDXMLURI_X_X, uri, XMLNS);
545545

@@ -697,7 +697,6 @@ private void schemaImport() throws QueryException {
697697
}
698698
final byte[] uri = uriLiteral();
699699
if(prefix != null && uri.length == 0) throw error(NSEMPTY);
700-
if(!Uri.get(uri).isValid()) throw error(INVURI_X, uri);
701700
addLocations(new TokenList());
702701
throw error(IMPLSCHEMA);
703702
}
@@ -715,7 +714,6 @@ private void moduleImport() throws QueryException {
715714

716715
final byte[] uri = uriLiteral();
717716
if(uri.length == 0) throw error(NSMODURI);
718-
if(!Uri.get(uri).isValid()) throw error(INVURI_X, uri);
719717
if(sc.imports.contains(token(uri))) throw error(DUPLMODULE_X, uri);
720718
sc.imports.add(uri);
721719

@@ -754,8 +752,7 @@ private boolean addLocations(final TokenList list) throws QueryException {
754752
if(add) {
755753
do {
756754
final byte[] uri = uriLiteral();
757-
if(!Uri.get(uri).isValid() || IO.get(string(uri)) instanceof IOContent)
758-
throw error(INVURI_X, uri);
755+
if(IO.get(string(uri)) instanceof IOContent) throw error(INVURI_X, uri);
759756
list.add(uri);
760757
} while(wsConsume(","));
761758
}
@@ -2467,7 +2464,8 @@ private ExprInfo simpleNodeTest(final Kind kind, final boolean all) throws Query
24672464
name = new QNm(concat(name.string(), cpToken(':')));
24682465
scope = NameTest.Scope.URI;
24692466
} else if(!eqName) {
2470-
scope = NameTest.Scope.FLEXIBLE;
2467+
scope = kind == Kind.ELEMENT && eq(sc.elemNS, ANY_URI) ? NameTest.Scope.LOCAL
2468+
: NameTest.Scope.FLEXIBLE;
24712469
}
24722470
}
24732471
// name test: prefix:name, name, Q{uri}name
@@ -2878,11 +2876,15 @@ private byte[] stringLiteral() throws QueryException {
28782876

28792877
/**
28802878
* Parses the "URILiteral" rule.
2879+
* @param special tokens that are allowed though not valid URI literals
28812880
* @return query expression
28822881
* @throws QueryException query exception
28832882
*/
2884-
private byte[] uriLiteral() throws QueryException {
2885-
return normalize(stringLiteral());
2883+
private byte[] uriLiteral(final byte[]... special) throws QueryException {
2884+
final byte[] uri = normalize(stringLiteral());
2885+
for(final byte[] sp : special) if(eq(uri, sp)) return uri;
2886+
if(!Uri.get(uri).isValid()) throw error(INVURI_X, uri);
2887+
return uri;
28862888
}
28872889

28882890
/**
@@ -2898,6 +2900,7 @@ private byte[] bracedURILiteral() throws QueryException {
28982900
entity(token);
28992901
}
29002902
final byte[] ns = normalize(token.toArray());
2903+
if(!Uri.get(ns).isValid()) throw error(INVURI_X, ns);
29012904
if(eq(ns, XMLNS_URI)) {
29022905
pos = p;
29032906
throw error(ILLEGALEQNAME_X, ns);
@@ -3471,7 +3474,7 @@ private SeqType castTarget() throws QueryException {
34713474
if(wsConsume("(")) {
34723475
type = choiceItemType().type;
34733476
} else {
3474-
final QNm name = eQName(sc.elemNS, TYPEINVALID);
3477+
final QNm name = eQName(eq(sc.elemNS, ANY_URI) ? XS_URI : sc.elemNS, TYPEINVALID);
34753478
if(!name.hasURI() && eq(name.local(), token(ENUM))) {
34763479
if(!wsConsume("(")) throw error(WHICHCAST_X, BasicType.similar(name));
34773480
type = enumerationType();
@@ -3560,8 +3563,8 @@ private SeqType itemType() throws QueryException {
35603563
}
35613564
}
35623565
} else {
3563-
// attach default element namespace
3564-
if(!name.hasURI()) name.uri(sc.elemNS);
3566+
// attach default element namespace, or schema namespace if default element namespace is ##any
3567+
if(!name.hasURI()) name.uri(eq(sc.elemNS, ANY_URI) ? XS_URI : sc.elemNS);
35653568
// basic type
35663569
type = BasicType.get(name, false);
35673570
// declared type

basex-core/src/main/java/org/basex/query/QueryText.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ public interface QueryText {
340340
/** BaseX URI. */ byte[] XQUERY_URI = token(BXMODULES_URL + "xquery");
341341
/** BaseX URI. */ byte[] XSLT_URI = token(BXMODULES_URL + "xslt");
342342

343+
/** "Any" URI. */ byte[] ANY_URI = token("##any");
344+
343345
// QUERY PLAN ===================================================================================
344346

345347
/** Query Info. */ String OP = "op";

basex-core/src/main/java/org/basex/query/util/parse/QNmResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void resolve(final QueryParser qp, final int npos, final byte[] elemNS)
6464
entry.name.uri(qp.qc.ns.resolve(entry.name.prefix(), qp.sc));
6565
if(npos == 0 && !entry.name.hasURI())
6666
throw qp.error(NOURI_X, entry.info, entry.name.prefix());
67-
} else if(entry.nsElem) {
67+
} else if(entry.nsElem && !Token.eq(elemNS, QueryText.ANY_URI)) {
6868
entry.name.uri(elemNS);
6969
}
7070
if(entry.name.hasURI()) entries.remove(i);

basex-core/src/test/java/org/basex/query/NamespaceTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,48 @@ public final class NamespaceTest extends SandboxTest {
701701
"<a xmlns=\"a\"/>");
702702
}
703703

704+
/**
705+
* Tests with default element namespace set to ##any.
706+
*/
707+
@Test public void defaultElementNamespaceAny() {
708+
final String defAny = "declare default element namespace '##any';\n";
709+
710+
// element-axis NameTest => *:a
711+
query(defAny + "<a/>/self::a", "<a/>");
712+
query(defAny + "element Q{a}a {}/self::a", "<a xmlns=\"a\"/>");
713+
query(defAny + "element Q{a}a {}/self::Q{}a", "");
714+
query(defAny + "<r xmlns:x='X' xmlns:y='Y'><a/><x:a/><y:a/></r>/a",
715+
"<a/>\n<x:a xmlns:x=\"X\"/>\n<y:a xmlns:y=\"Y\"/>");
716+
query(defAny + "<r xmlns:x='X'><x:a/></r>/*[self::a]", "<x:a xmlns:x=\"X\"/>");
717+
718+
// attributes are unaffected
719+
query(defAny + "count(<e xmlns:x='X' a='' x:a=''/>/@a)", 1);
720+
query(defAny + "count(<e xmlns:x='X' a='' x:a=''/>/@*)", 2);
721+
722+
// element(...) node tests
723+
query(defAny + "<a/> instance of element(a)", true);
724+
query(defAny + "<a xmlns='X'/> instance of element(a)", true);
725+
query(defAny + "<a/> instance of element(Q{}a)", true);
726+
query(defAny + "element Q{X}a {} instance of element(Q{}a)", false);
727+
query(defAny + "document { element Q{X}a {} } instance of document-node(element(a))", true);
728+
729+
// unprefixed type names => xs:*
730+
query(defAny + "42 instance of integer", true);
731+
query(defAny + "'42' cast as integer", 42);
732+
733+
// other contexts fall back to no namespace
734+
query(defAny + "namespace-uri(element a {})", "");
735+
query(defAny + "<r xmlns:x='X'><a/><x:a/></r>/Q{}a", "<a/>");
736+
737+
// with fixed + ##any, constructor xmlns must not suppress ##any behavior
738+
query("declare fixed default element namespace '##any'; " +
739+
"<a xmlns='u'><b/>{ <u:c xmlns:u='u'/>/self::c }</a>",
740+
"<a xmlns=\"u\"><b/><u:c xmlns:u=\"u\"/></a>");
741+
742+
// ##any in xmlns context
743+
error("<a xmlns='##any'/>", INVURI_X);
744+
}
745+
704746
/**
705747
* Test query.
706748
* Detects malformed namespace hierarchy.

0 commit comments

Comments
 (0)