Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions basex-core/src/main/java/org/basex/query/QueryParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public class QueryParser extends InputParser {
/** Output declarations. */
private final HashMap<String, Object> sparams = new HashMap<>();
/** QName cache. */
private final QNmCache qnames = new QNmCache();
private final QNmResolver qnames = new QNmResolver();
/** Local variable. */
private final LocalVars localVars = new LocalVars(this);

Expand Down Expand Up @@ -261,7 +261,7 @@ private void finish(final MainModule mm) throws QueryException {
}

// completes the parsing step
qnames.assignURI(this, 0);
qnames.resolve(this, 0, sc.elemNS);
if(sc.elemNS != null) sc.ns.add(EMPTY, sc.elemNS, null);
RecordType.resolveRefs(recordTypeRefs, namedRecordTypes);
}
Expand Down Expand Up @@ -329,8 +329,10 @@ private void prolog1() throws QueryException {
while(true) {
final int p = pos;
if(wsConsumeWs(DECLARE)) {
if(wsConsumeWs(DEFAULT)) {
if(!defaultNamespaceDecl() && !defaultCollationDecl() && !emptyOrderDecl() &&
if(wsConsumeWs(FIXED)) {
if(!wsConsumeWs(DEFAULT) || !defaultNamespaceDecl(true)) throw error(DECLINCOMPLETE);
} else if(wsConsumeWs(DEFAULT)) {
if(!defaultNamespaceDecl(false) && !defaultCollationDecl() && !emptyOrderDecl() &&
!decimalFormatDecl(true)) throw error(DECLINCOMPLETE);
} else if(wsConsumeWs(BOUNDARY_SPACE)) {
boundarySpaceDecl();
Expand Down Expand Up @@ -529,10 +531,11 @@ private void boundarySpaceDecl() throws QueryException {

/**
* Parses the "DefaultNamespaceDecl" rule.
* @param fixed fixed default namespace flag
* @return true if declaration was found
* @throws QueryException query exception
*/
private boolean defaultNamespaceDecl() throws QueryException {
private boolean defaultNamespaceDecl(final boolean fixed) throws QueryException {
final boolean elem = wsConsumeWs(ELEMENT);
if(!elem && !wsConsumeWs(FUNCTION)) return false;
wsCheck(NAMESPACE);
Expand All @@ -542,7 +545,9 @@ private boolean defaultNamespaceDecl() throws QueryException {

if(elem) {
if(!decl.add(ELEMENT)) throw error(DUPLNS);
sc.elemNsFixed = fixed;
sc.elemNS = uri.length == 0 ? null : uri;
sc.dirNS = sc.elemNS;
} else {
if(!decl.add(FUNCTION)) throw error(DUPLNS);
sc.funcNS = uri;
Expand Down Expand Up @@ -682,9 +687,13 @@ private void schemaImport() throws QueryException {
prefix = ncName(NONAME_X, false);
if(eq(prefix, XML, XMLNS)) throw error(BINDXML_X, prefix);
wsCheck("=");
} else if(wsConsumeWs(DEFAULT)) {
wsCheck(ELEMENT);
wsCheck(NAMESPACE);
} else {
final boolean fixed = wsConsumeWs(FIXED);
if(fixed || wsConsumeWs(DEFAULT)) {
if(fixed) wsCheck(DEFAULT);
wsCheck(ELEMENT);
wsCheck(NAMESPACE);
}
}
final byte[] uri = uriLiteral();
if(prefix != null && uri.length == 0) throw error(NSEMPTY);
Expand Down Expand Up @@ -2080,7 +2089,7 @@ private void validate() throws QueryException {

if(consume(TYPE)) {
final InputInfo ii = info();
qnames.add(eQName(SKIPCHECK, QNAME_X), ii);
qnames.add(eQName(SKIPCHECK, QNAME_X), sc, ii);
}
consume(STRICT);
consume(LAX);
Expand Down Expand Up @@ -2462,7 +2471,7 @@ private ExprInfo simpleNodeTest(final Kind kind, final boolean all) throws Query
}
}
// name test: prefix:name, name, Q{uri}name
qnames.add(name, kind == Kind.ELEMENT, ii);
if(kind == Kind.ELEMENT) qnames.add(name, sc, ii); else qnames.add(name, false, ii);
return nameTest.apply(name, scope);
}
}
Expand Down Expand Up @@ -3077,10 +3086,11 @@ private Expr dirElement(final boolean root) throws QueryException {
// cache namespace information
final int size = sc.ns.size();
final byte[] nse = sc.elemNS;
final byte[] nsd = sc.dirNS;
final int npos = qnames.size();

final QNm name = new QNm(qnm);
qnames.add(name, ii);
qnames.add(name, true, ii);

final Atts ns = new Atts();
final ExprList cont = new ExprList();
Expand Down Expand Up @@ -3170,7 +3180,8 @@ private Expr dirElement(final boolean root) throws QueryException {
sc.ns.add(prefix, uri);
} else {
if(eq(uri, XML_URI)) throw error(XMLNSDEF_X, uri);
sc.elemNS = uri;
sc.dirNS = uri;
if(!sc.elemNsFixed) sc.elemNS = sc.dirNS;
}
if(ns.contains(prefix)) throw error(DUPLNSDEF_X, prefix);
ns.add(prefix, uri);
Expand Down Expand Up @@ -3201,7 +3212,7 @@ private Expr dirElement(final boolean root) throws QueryException {
if(!eq(name.string(), close)) throw error(TAGWRONG_X_X, name.string(), close);
}

qnames.assignURI(this, npos);
qnames.resolve(this, npos, sc.dirNS);

// check for duplicate attribute names
if(atts != null) {
Expand All @@ -3215,6 +3226,7 @@ private Expr dirElement(final boolean root) throws QueryException {

sc.ns.size(size);
sc.elemNS = nse;
sc.dirNS = nsd;
return new CElem(info(), false, name, ns, cont.finish());
}

Expand Down Expand Up @@ -3356,7 +3368,7 @@ private Expr compConstructor() throws QueryException {
private Expr compElement() throws QueryException {
final Expr name = compName(NOELEMNAME, true);
if(name == null) return null;
if(name instanceof final QNm qnm) qnames.add(qnm, info());
if(name instanceof final QNm qnm) qnames.add(qnm, sc, info());
skipWs();
return current('{') ? new CElem(info(), true, name, new Atts(), enclosedExpr()) : null;
}
Expand Down
1 change: 1 addition & 0 deletions basex-core/src/main/java/org/basex/query/QueryText.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public interface QueryText {
/** Parser token. */ String FALSE = "false";
/** Parser token. */ String FINALLY = "finally";
/** Parser token. */ String FIRST = "first";
/** Parser token. */ String FIXED = "fixed";
/** Parser token. */ String FN = "fn";
/** Parser token. */ String FOR = "for";
/** Parser token. */ String FROM = "from";
Expand Down
5 changes: 5 additions & 0 deletions basex-core/src/main/java/org/basex/query/StaticContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ public final class StaticContext {

/** Default collation (default collection ({@link QueryText#COLLATION_URI}): {@code null}). */
public Collation collation;
/** Default element/type namespace fixed flag. */
public boolean elemNsFixed;
/** Default element/type namespace. */
public byte[] elemNS;
/** Direct element constructor default element/type namespace (differs from above if "fixed"). */
public byte[] dirNS;
/** Default function namespace. */
public byte[] funcNS;
/** Name of module (not assigned for main module). */
Expand Down Expand Up @@ -90,6 +94,7 @@ public StaticContext(final QueryContext qc) {
void namespace(final String prefix, final String uri) throws QueryException {
if(prefix.isEmpty()) {
elemNS = uri.isEmpty() ? null : token(uri);
dirNS = elemNS;
} else if(uri.isEmpty()) {
ns.delete(token(prefix));
} else {
Expand Down
57 changes: 0 additions & 57 deletions basex-core/src/main/java/org/basex/query/util/parse/QNmCache.java

This file was deleted.

53 changes: 0 additions & 53 deletions basex-core/src/main/java/org/basex/query/util/parse/QNmCheck.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.basex.query.util.parse;

import static org.basex.query.QueryError.*;

import java.util.*;

import org.basex.query.*;
import org.basex.query.value.item.*;
import org.basex.util.*;

/**
* Resolves namespace URIs of QNames whose resolution must be deferred until the surrounding
* namespace context is known.
*
* @author BaseX Team, BSD License
* @author Christian Gruen
*/
public final class QNmResolver {
/**
* Entry for a QName whose namespace URI still needs to be resolved.
* @param name QName to be resolved
* @param nsElem flag for assigning default element namespace
* @param info input info (can be {@code null})
*/
private record Entry(QNm name, boolean nsElem, InputInfo info) { }
/** QNames to be resolved. */
private final ArrayList<Entry> entries = new ArrayList<>();

/**
* Adds a QName unless it already has a namespace URI or it can be immediately assigned the fixed
* default namespace URI. This method must not be called for the element names of direct element
* constructors.
* @param name QName
* @param sc static context
* @param info input info (can be {@code null})
*/
public void add(final QNm name, final StaticContext sc, final InputInfo info) {
if(sc.elemNsFixed && !name.hasPrefix() && !name.hasURI()) name.uri(sc.elemNS);
else add(name, true, info);
}

/**
* Adds a QName unless it already has a namespace URI.
* @param name qname
* @param nsElem default check
* @param info input info (can be {@code null})
*/
public void add(final QNm name, final boolean nsElem, final InputInfo info) {
if(!name.hasURI()) entries.add(new Entry(name, nsElem, info));
}

/**
* Finalizes the QNames by assigning namespace URIs.
* @param qp query parser
* @param npos first entry to be checked
* @param elemNS default element namespace
* @throws QueryException query exception
*/
public void resolve(final QueryParser qp, final int npos, final byte[] elemNS)
throws QueryException {
for(int i = entries.size() - 1; i >= npos; --i) {
final Entry entry = entries.get(i);
if(entry.name.hasPrefix()) {
entry.name.uri(qp.sc.ns.uri(entry.name.prefix()));
if(npos == 0 && !entry.name.hasURI())
throw qp.error(NOURI_X, entry.info, entry.name.prefix());
} else if(entry.nsElem) {
entry.name.uri(elemNS);
}
if(entry.name.hasURI()) entries.remove(i);
}
}

/**
* Returns the number of remaining QNames.
* @return number
*/
public int size() {
return entries.size();
}
}
Loading