package org.basex.query;
import static org.basex.query.QueryText.*;
import static org.basex.util.Token.*;
import org.basex.core.*;
import org.basex.io.*;
import org.basex.query.util.*;
import org.basex.query.util.collation.*;
import org.basex.query.util.format.*;
import org.basex.query.value.item.*;
import org.basex.query.value.type.*;
import org.basex.util.*;
import org.basex.util.hash.*;
/**
* This class contains the static context of an expression.
*
* @author BaseX Team 2005-17, BSD License
* @author Christian Gruen
*/
public final class StaticContext {
/** Decimal formats. */
public final TokenObjMap<DecFormatter> decFormats = new TokenObjMap<>();
/** Static and dynamic namespaces. */
public final NSContext ns = new NSContext();
/** Mix updates flag. */
public final boolean mixUpdates;
/** Default collation (default collection ({@link QueryText#COLLATION_URI}): {@code null}). */
public Collation collation;
/** Default element/type namespace. */
public byte[] elemNS;
/** Default function namespace. */
public byte[] funcNS = FN_URI;
/** Expression contains dynamic function call. */
public boolean dynFuncCall;
/** Static type of context value. */
SeqType contextType;
/** Construction mode. */
public boolean strip;
/** Ordering mode. */
public boolean ordered = true;
/** Default order for empty sequences. */
public boolean orderGreatest;
/** Boundary-space policy. */
public boolean spaces;
/** Copy-namespaces mode: (no-)preserve. */
public boolean preserveNS = true;
/** Copy-namespaces mode: (no-)inherit. */
public boolean inheritNS = true;
/** Static Base URI. */
private Uri baseURI = Uri.EMPTY;
/** Sets a module URI resolver. */
UriResolver resolver;
/**
* Constructor.
* @param qc query context
*/
public StaticContext(final QueryContext qc) {
mixUpdates = qc.context.options.get(MainOptions.MIXUPDATES);
}
/**
* Declares a namespace.
* A namespace is undeclared if the specified URI is an empty string.
* The default element namespaces is set if the specified prefix is empty.
* @param prefix namespace prefix
* @param uri namespace URI
* @throws QueryException query exception
*/
void namespace(final String prefix, final String uri) throws QueryException {
if(prefix.isEmpty()) {
elemNS = uri.isEmpty() ? null : token(uri);
} else if(uri.isEmpty()) {
ns.delete(token(prefix));
} else {
ns.add(token(prefix), token(uri), null);
}
}
/**
* Returns the static base URI.
* @return base URI
*/
public Uri baseURI() {
return baseURI;
}
/**
* Sets the static base URI.
* @param uri uri to be set: an empty URI will be ignored, {@code null} invalidates the URI
*/
public void baseURI(final String uri) {
String string = "";
if(uri != null) {
// ignore empty URIs
if(uri.isEmpty()) return;
// adopt original URIs that do not adhere to a known IO schema
string = IO.get(uri) instanceof IOContent ? uri : resolve(uri).url();
// #1062: check if specified URI points to a directory. if yes, add trailing slash
if(!string.endsWith("/") && (uri.endsWith(".") || uri.endsWith("/"))) string += '/';
}
baseURI = Uri.uri(string);
}
/**
* Returns an IO representation of the static base URI or {@code null}.
* @return IO reference (can be {@code null})
*/
public IO baseIO() {
return baseURI == Uri.EMPTY ? null : IO.get(string(baseURI.string()));
}
/**
* Resolves the specified path against the base URI.
* @param path to be resolved
* @return resulting path
*/
public IO resolve(final String path) {
final IO baseIO = baseIO();
return baseIO == null ? IO.get(path) : baseIO.merge(path);
}
/**
* Returns an IO reference for the specified path.
* If a base URI exists, it is merged with the path.
* @param path file path
* @param uri module namespace (can be {@code null}, only relevant for custom resolver)
* @return io reference
*/
public IO resolve(final String path, final String uri) {
return resolver == null ? resolve(path) : resolver.resolve(path, uri, baseURI);
}
/**
* Returns a decimal format.
* @param id format id
* @return decimal format, or {@code null}
* @throws QueryException query exception
*/
public DecFormatter decFormat(final byte[] id) throws QueryException {
DecFormatter df = decFormats.get(id);
if(df == null && eq(id, EMPTY)) {
// lazy instantiation of default decimal format
df = new DecFormatter(null, null);
decFormats.put(id, df);
}
return df;
}
@Override
public String toString() {
return Util.className(this) + '[' + baseIO() + ']';
}
}