package org.basex.query;
import static org.basex.core.Text.*;
import static org.basex.query.QueryError.*;
import java.util.*;
import org.basex.query.expr.*;
import org.basex.query.value.*;
import org.basex.query.value.item.*;
import org.basex.query.value.seq.*;
import org.basex.util.*;
import org.basex.util.list.*;
/**
* Thrown to indicate an exception during the parsing or evaluation of a query.
*
* @author BaseX Team 2005-17, BSD License
* @author Christian Gruen
*/
public class QueryException extends Exception {
/** Static exception. */
static final QueryException ERROR = new QueryException("") {
@Override
public synchronized Throwable fillInStackTrace() { return this; }
};
/** Stack. */
private final ArrayList<InputInfo> stack = new ArrayList<>();
/** Error QName. */
private final QNm name;
/** Error value. */
private Value value = Empty.SEQ;
/** Error reference. */
private QueryError error;
/** Code suggestions. */
private StringList suggest;
/** Error line and column. */
private InputInfo info;
/** Marked error column. */
private int markedCol;
/** Marks if this exception is catchable by a {@code try/catch} expression. */
private boolean catchable = true;
/**
* Constructor, specifying an exception or error. {@link QueryError#BASX_GENERIC_X} will be set
* as error code.
* @param cause exception or error
*/
public QueryException(final Throwable cause) {
this(Util.message(cause));
initCause(cause);
}
/**
* Constructor, specifying a simple error message. {@link QueryError#BASX_GENERIC_X} will be set
* as error code.
* @param message error message
*/
public QueryException(final String message) {
this(null, BASX_GENERIC_X, message);
}
/**
* Default constructor.
* @param info input info
* @param error error reference
* @param ext error extension
*/
public QueryException(final InputInfo info, final QueryError error, final Object... ext) {
this(info, error.qname(), error.desc, ext);
this.error = error;
}
/**
* Constructor, specifying the error code and message as string.
* @param info input info
* @param name error code
* @param message error message
* @param ext error extension
*/
public QueryException(final InputInfo info, final QNm name, final String message,
final Object... ext) {
super(message(message, ext));
this.name = name;
if(info != null) info(info);
for(final Object o : ext) {
if(o instanceof Throwable) {
initCause((Throwable) o);
break;
}
}
}
/**
* Returns the error column.
* @return error column
*/
public final int column() {
return info == null ? 0 : info.column();
}
/**
* Returns the marked error column.
* @return marked error column
*/
public final int markedColumn() {
return markedCol;
}
/**
* Returns the error line.
* @return error line
*/
public final int line() {
return info == null ? 0 : info.line();
}
/**
* Returns the file.
* @return error line
*/
public final String file() {
return info == null ? null : info.path();
}
/**
* Returns suggestions for code suggestions.
* @return suggestions
*/
public final StringList suggest() {
return suggest == null ? new StringList() : suggest;
}
/**
* Sets code suggestions.
* @param qp query parser
* @param sug code suggestions
* @return self reference
*/
public final QueryException suggest(final InputParser qp, final StringList sug) {
suggest = sug;
pos(qp);
return this;
}
/**
* Adds an input info to the stack.
* @param ii input info
* @return self reference
*/
public final QueryException add(final InputInfo ii) {
if(ii != null) stack.add(ii);
return this;
}
/**
* Sets input info.
* @param ii input info
* @return self reference
*/
public final QueryException info(final InputInfo ii) {
info = ii;
return this;
}
/**
* Returns the input info.
* @return input info
*/
public final InputInfo info() {
return info;
}
/**
* Sets the error value.
* @param val error value
* @return self reference
*/
public final QueryException value(final Value val) {
value = val;
return this;
}
/**
* Sets an error.
* @param err error
* @return self reference
*/
final QueryException error(final QueryError err) {
error = err;
return this;
}
/**
* Finds line and column for the specified query parser.
* @param parser parser
*/
final void pos(final InputParser parser) {
markedCol = parser.mark;
if(info != null) return;
// check if line/column information has already been added
parser.pos = Math.min(parser.mark, parser.length);
info = new InputInfo(parser);
}
/**
* Returns the error name.
* @return error name
*/
public final QNm qname() {
return name;
}
/**
* Returns the error.
* @return error
*/
public final QueryError error() {
return error;
}
/**
* Returns the error value.
* @return error value
*/
public final Value value() {
return value;
}
@Override
public String getLocalizedMessage() {
return super.getMessage();
}
@Override
public String getMessage() {
final TokenBuilder tb = new TokenBuilder();
if(info != null) tb.add(STOPPED_AT).add(info.toString()).add(COL).add(NL);
final byte[] code = name.local();
if(code.length != 0) tb.add('[').add(name.prefixId(QueryText.ERROR_URI)).add("] ");
tb.add(getLocalizedMessage());
if(!stack.isEmpty()) {
tb.add(NL).add(NL).add(STACK_TRACE).add(COL);
for(final InputInfo ii : stack) tb.add(NL).add(LI).add(ii.toString());
}
return tb.toString();
}
/**
* Checks if this exception can be caught by a {@code try/catch} expression.
* @return result of check
*/
public boolean isCatchable() {
return catchable;
}
/**
* Makes this exception uncatchable by a {@code try/catch} expression.
* @return self reference for convenience
*/
public final QueryException notCatchable() {
catchable = false;
return this;
}
/**
* Creates the error message from the specified text and extension array.
* @param text text message with optional placeholders
* @param ext info extensions
* @return argument
*/
private static String message(final String text, final Object[] ext) {
final int es = ext.length;
for(int e = 0; e < es; e++) {
if(ext[e] instanceof ExprInfo) ext[e] = chop(((ExprInfo) ext[e]).toErrorString(), null);
}
return Util.info(text, ext);
}
}