Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.basex.data;
import static org.basex.core.Text.*;
import static org.basex.data.DataText.*;
import java.io.*;
import java.util.*;
import org.basex.io.in.DataInput;
import org.basex.io.out.DataOutput;
import org.basex.util.*;
import org.basex.util.hash.*;
import org.basex.util.list.*;
/**
* This class organizes the namespaces of a database.
*
* @author BaseX Team 2005-22, BSD License
* @author Christian Gruen
*/
public final class Namespaces {
/** Namespace prefixes. */
private final TokenSet prefixes;
/** Namespace URIs. */
private final TokenSet uris;
/** Root node. */
private final NSNode root;
/** Stack with references to current default namespaces. */
private final IntList defaults = new IntList(2);
/** Current level. Index starts at 1 (required by XQUF operations). */
private int level = 1;
/** Current namespace node. */
private NSNode current;
// Creating and Writing Namespaces ==============================================================
/**
* Empty constructor.
*/
public Namespaces() {
prefixes = new TokenSet();
uris = new TokenSet();
root = new NSNode(-1);
current = root;
}
/**
* Constructor, specifying an input stream.
* @param in input stream
* @throws IOException I/O exception
*/
Namespaces(final DataInput in) throws IOException {
prefixes = new TokenSet(in);
uris = new TokenSet(in);
root = new NSNode(in, null);
current = root;
}
/**
* Writes the namespaces to disk.
* @param out output stream
* @throws IOException I/O exception
*/
void write(final DataOutput out) throws IOException {
prefixes.write(out);
uris.write(out);
root.write(out);
}
// Requesting Namespaces Globally ===============================================================
/**
* Returns if no namespaces exist.
* Note that the container size does not change if namespaces are deleted.
* This function is mainly used to decide namespaces need to be considered in query optimizations.
* @return result of check
*/
public boolean isEmpty() {
return uris.isEmpty();
}
/**
* Returns the number of namespaces that have been stored so far.
* @return number of entries
*/
public int size() {
return uris.size();
}
/**
* Returns a prefix for the name with the specified id.
* @param id id of prefix
* @return prefix
*/
byte[] prefix(final int id) {
return prefixes.key(id);
}
/**
* Returns a namespace URI for the name with the specified id.
* @param id id of namespace URI ({@code 0}: no namespace)
* @return namespace URI or {@code null}
*/
public byte[] uri(final int id) {
return uris.key(id);
}
/**
* Returns the id of the specified namespace uri.
* @param uri namespace URI
* @return id, or {@code 0} if no entry is found
*/
public int uriId(final byte[] uri) {
return uris.id(uri);
}
/**
* Returns the common default namespace of all documents of the database.
* @param ndocs number of documents
* @return namespace, or {@code null} if there is no common namespace
*/
byte[] defaultNs(final int ndocs) {
// no namespaces defined: default namespace is empty
final int ch = root.children();
if(ch == 0) return Token.EMPTY;
// give up if number of default namespaces differs from number of documents
if(ch != ndocs) return null;
int id = 0;
for(int c = 0; c < ch; c++) {
final NSNode child = root.child(0);
final int[] values = child.values();
// give up if child node has more children or more than one namespace
// give up if namespace has a non-empty prefix
if(child.children() > 0 || child.pre() != 1 || values.length != 2 ||
prefix(values[0]).length != 0) return null;
// check if all documents have the same default namespace
if(c == 0) {
id = values[1];
} else if(id != values[1]) {
return null;
}
}
// return common default namespace
return uri(id);
}
// Requesting Namespaces Based on Context =======================================================
/**
* Returns the id of a namespace URI for the specified prefix.
* @param prefix prefix
* @param element indicates if the prefix belongs to an element or attribute name
* @return id of namespace uri, or {@code 0} if no entry is found
*/
public int uriIdForPrefix(final byte[] prefix, final boolean element) {
if(isEmpty()) return 0;
return prefix.length == 0 ? element ? defaults.get(level) : 0 : uriId(prefix, current);
}
/**
* Returns the id of a namespace URI for the specified prefix and pre value.
* @param prefix prefix
* @param pre pre value
* @param data data reference
* @return id of namespace uri, or {@code 0} if no entry is found
*/
public int uriIdForPrefix(final byte[] prefix, final int pre, final Data data) {
return uriId(prefix, current.find(pre, data));
}
/**
* Returns the id of a namespace URI for the specified prefix and node.
* @param prefix prefix
* @param node node to start with
* @return id of the namespace uri, or {@code 0} if namespace is not found
*/
private int uriId(final byte[] prefix, final NSNode node) {
final int prefId = prefixes.id(prefix);
if(prefId == 0) return 0;
NSNode nd = node;
while(nd != null) {
final int uriId = nd.uri(prefId);
if(uriId != 0) return uriId;
nd = nd.parent();
}
return 0;
}
/**
* Returns all namespace prefixes and uris that are declared for the specified pre value.
* Should only be called for element nodes.
* @param pre pre value
* @param data data reference
* @return key and value ids
*/
Atts values(final int pre, final Data data) {
final int[] values = current.find(pre, data).values();
final int nl = values.length;
final Atts as = new Atts(nl / 2);
for(int n = 0; n < nl; n += 2) as.add(prefix(values[n]), uri(values[n + 1]));
return as;
}
/**
* Finds the nearest namespace node on the ancestor axis of the insert location and sets it as new
* root. Possible candidates for this node are collected and the match with the highest pre value
* between ancestors and candidates is determined.
* @param pre pre value
* @param data data reference
*/
void root(final int pre, final Data data) {
// collect possible candidates for namespace root
final List cand = new LinkedList<>();
NSNode nd = root;
cand.add(nd);
for(int p; (p = nd.find(pre)) > -1;) {
// add candidate to stack
nd = nd.child(p);
cand.add(0, nd);
}
nd = root;
if(cand.size() > 1) {
// compare candidates to ancestors of pre value
int ancPre = pre;
// take first candidate from stack
NSNode curr = cand.remove(0);
while(ancPre > -1 && nd == root) {
// if the current candidate's pre value is lower than the current ancestor of par or par
// itself, we have to look for a potential match for this candidate. therefore we iterate
// through ancestors until we find one with a lower than or the same pre value as the
// current candidate.
while(ancPre > curr.pre()) ancPre = data.parent(ancPre, data.kind(ancPre));
// this is the new root
if(ancPre == curr.pre()) nd = curr;
// no potential for infinite loop, because dummy root is always a match,
// in this case ancPre ends iteration
if(!cand.isEmpty()) curr = cand.remove(0);
}
}
final int uriId = uriIdForPrefix(Token.EMPTY, pre, data);
defaults.set(level, uriId);
// remember uri before insert of first node n to connect siblings of n to according namespace
defaults.set(level - 1, uriId);
current = nd;
}
/**
* Caches and returns all namespace nodes in the namespace structure with a minimum pre value.
* @param pre minimum pre value of a namespace node
* @return list of namespace nodes
*/
ArrayList cache(final int pre) {
final ArrayList list = new ArrayList<>();
addNodes(root, list, pre);
return list;
}
/**
* Recursively adds namespace nodes to a list, starting with the children of a node.
* @param node current namespace node
* @param list list with namespace nodes
* @param pre pre value
*/
private static void addNodes(final NSNode node, final List list, final int pre) {
final int size = node.children();
int n = Math.max(0, node.find(pre));
while(n > 0 && (n == size || node.child(n).pre() >= pre)) n--;
for(; n < size; n++) {
final NSNode child = node.child(n);
if(child.pre() >= pre) list.add(child);
addNodes(child, list, pre);
}
}
// Updating Namespaces ==========================================================================
/**
* Sets a namespace cursor.
* @param node namespace node
*/
void cursor(final NSNode node) {
current = node;
}
/**
* Returns the current namespace cursor.
* @return current namespace node
*/
NSNode cursor() {
return current;
}
/**
* Increases the level counter and sets a new default namespace.
*/
public void open() {
final int nu = defaults.get(level);
defaults.set(++level, nu);
}
/**
* Adds namespaces to a new namespace child node and sets this node as new cursor.
* @param pre pre value
* @param atts namespaces
*/
public void open(final int pre, final Atts atts) {
open();
if(!atts.isEmpty()) {
final NSNode nd = new NSNode(pre);
current.add(nd);
current = nd;
final int as = atts.size();
for(int a = 0; a < as; a++) {
final byte[] pref = atts.name(a), uri = atts.value(a);
final int prefId = prefixes.put(pref), uriId = uris.put(uri);
nd.add(prefId, uriId);
if(pref.length == 0) defaults.set(level, uriId);
}
}
}
/**
* Adds a single namespace for the specified pre value.
* @param pre pre value
* @param prefix prefix
* @param uri namespace uri
* @param data data reference
* @return id of namespace uri
*/
public int add(final int pre, final byte[] prefix, final byte[] uri, final Data data) {
final int prefId = prefixes.put(prefix), uriId = uris.put(uri);
NSNode nd = current.find(pre, data);
if(nd.pre() != pre) {
final NSNode child = new NSNode(pre);
nd.add(child);
nd = child;
}
nd.add(prefId, uriId);
return uriId;
}
/**
* Closes a namespace node.
* @param pre current pre value
*/
public void close(final int pre) {
while(current.pre() >= pre) {
final NSNode nd = current.parent();
if(nd == null) break;
current = nd;
}
--level;
}
/**
* Deletes the specified namespace URI from the root node.
* @param uri namespace URI reference
*/
public void delete(final byte[] uri) {
final int id = uris.id(uri);
if(id != 0) current.delete(id);
}
/**
* Deletes the specified number of entries from the namespace structure.
* @param pre pre value of the first node to delete
* @param data data reference
* @param size number of entries to be deleted
*/
void delete(final int pre, final int size, final Data data) {
NSNode nd = current.find(pre, data);
if(nd.pre() == pre) nd = nd.parent();
while(nd != null) {
nd.delete(pre, size);
nd = nd.parent();
}
root.decrementPre(pre, size);
}
// Printing Namespaces ==========================================================================
/**
* Returns a tabular representation of the namespace entries.
* @param start first pre value
* @param end last pre value
* @return namespaces
*/
byte[] table(final int start, final int end) {
if(root.children() == 0) return Token.EMPTY;
final Table t = new Table();
t.header.add(TABLENS);
t.header.add(TABLEPRE);
t.header.add(TABLEDIST);
t.header.add(TABLEPREF);
t.header.add(TABLEURI);
for(int i = 0; i < 3; ++i) t.align.add(true);
root.table(t, start, end, this);
return t.contents.isEmpty() ? Token.EMPTY : t.finish();
}
/**
* Returns namespace information.
* @return info string
*/
public byte[] info() {
final TokenObjMap map = new TokenObjMap<>();
root.info(map, this);
final TokenBuilder tb = new TokenBuilder();
for(final byte[] key : map) {
tb.add(" ");
final TokenList values = map.get(key).sort();
final int ks = values.size();
if(ks > 1 || values.get(0).length != 0) {
if(values.size() != 1) tb.add("(");
for(int k = 0; k < ks; ++k) {
if(k != 0) tb.add(", ");
tb.add(values.get(k));
}
if(ks != 1) tb.add(")");
tb.add(" = ");
}
tb.addExt("\"%\"" + NL, key);
}
return tb.finish();
}
/**
* Returns a string representation of the namespaces.
* @param start start pre value
* @param end end pre value
* @return string
*/
String toString(final int start, final int end) {
return root.toString(this, start, end);
}
@Override
public String toString() {
return toString(0, Integer.MAX_VALUE);
}
}