package org.basex.http.restxq;
import static org.basex.query.QueryError.*;
import static org.basex.util.Token.*;
import java.io.*;
import java.util.*;
import org.basex.core.*;
import org.basex.http.*;
import org.basex.io.*;
import org.basex.query.*;
import org.basex.query.func.*;
/**
* This class caches information on a single XQuery module with RESTXQ annotations.
*
* @author BaseX Team 2005-17, BSD License
* @author Christian Gruen
*/
final class RestXqModule {
/** Supported methods. */
private final ArrayList<RestXqFunction> functions = new ArrayList<>();
/** File reference. */
private final IOFile file;
/** Parsing timestamp. */
private long time;
/**
* Constructor.
* @param file xquery file
*/
RestXqModule(final IOFile file) {
this.file = file;
time = file.timeStamp();
}
/**
* Checks the module for RESTXQ annotations.
* @param ctx database context
* @return {@code true} if module contains relevant annotations
* @throws Exception exception (including unexpected ones)
*/
boolean parse(final Context ctx) throws Exception {
functions.clear();
// loop through all functions
try(QueryContext qc = qc(ctx)) {
// loop through all functions
final String name = file.name();
for(final StaticFunc sf : qc.funcs.funcs()) {
// only add functions that are defined in the same module (file)
if(sf.expr != null && name.equals(new IOFile(sf.info.path()).name())) {
final RestXqFunction rxf = new RestXqFunction(sf, qc, this);
if(rxf.parse(ctx)) functions.add(rxf);
}
}
}
return !functions.isEmpty();
}
/**
* Checks if the timestamp is still up-to-date.
* @return result of check
*/
boolean uptodate() {
return time == file.timeStamp();
}
/**
* Updates the timestamp.
*/
void touch() {
time = file.timeStamp();
}
/**
* Returns all functions.
* @return functions
*/
ArrayList<RestXqFunction> functions() {
return functions;
}
/**
* Processes the HTTP request.
* @param conn HTTP connection
* @param func function to be processed
* @param qe query exception (optional)
* @throws Exception exception
*/
void process(final HTTPConnection conn, final RestXqFunction func, final QueryException qe)
throws Exception {
// create new XQuery instance
final Context ctx = conn.context;
try(QueryContext qc = qc(ctx)) {
final StaticFunc sf = find(qc, func.function);
// will only happen if file has been swapped between caching and parsing
if(sf == null) throw HTTPCode.NO_XQUERY.get();
final RestXqFunction rxf = new RestXqFunction(sf, qc, this);
rxf.parse(ctx);
new RestXqResponse(rxf, qc, conn).create(qe);
}
}
// PRIVATE METHODS ====================================================================
/**
* Retrieves a query context for the given module.
* @param ctx database context
* @return query context
* @throws Exception exception
*/
private QueryContext qc(final Context ctx) throws Exception {
final QueryContext qc = new QueryContext(ctx);
try {
qc.parse(string(file.read()), file.path(), null);
return qc;
} catch(final IOException ex) {
// may be triggered when reading the file
throw IOERR_X.get(null, ex);
}
}
/**
* Returns the specified function from the given query context.
* @param qc query context.
* @param func function to be found
* @return function
*/
private static StaticFunc find(final QueryContext qc, final StaticFunc func) {
for(final StaticFunc sf : qc.funcs.funcs()) {
if(func.info.equals(sf.info)) return sf;
}
return null;
}
}