/* --------------------------------------------------------- *
* __________ D E L T A S C R I P T *
* (_________() *
* / === / - A fast, dynamic scripting language *
* | == | - Version 4.13.11.0 *
* / === / - Developed by Adam R. Nelson *
* | = = | - 2011-2013 *
* / === / - Distributed under GNU LGPL v3 *
* (________() - http://github.com/ar-nelson/deltascript *
* *
* --------------------------------------------------------- */
package com.sector91.delta.script;
import static com.sector91.delta.script.objects.DS_Tag.tag;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.sector91.delta.script.instrs.DSInstr;
import com.sector91.delta.script.objects.DS_Object;
import com.sector91.delta.script.objects.DS_Tag;
import com.sector91.util.A;
import com.sector91.util.StringTemplate;
import com.sector91.util.StringUtil;
import com.sector91.util._;
/**
* <p>Any kind of exception that occurs during the execution of DeltaScript
* code, complete with message and tags.</p>
*
* <p>DeltaScript supports <em>tagged errors</em>: any DeltaScript error can
* have a set of {@link DS_Tag}s attached to it, and the {@code error} function
* will receive these tags as its second argument.</p>
*
* <p>As a general rule, code intended to be used from DeltaScript should never
* throw normal Java exceptions. All exceptions should be wrapped in {@code
* DScriptErr} objects, which can contain the instruction and context in which
* the error occurred.
* </p>
*
* @author Adam R. Nelson
* @version 4.13.11.0
*/
public class DScriptErr extends RuntimeException
{
private static final long serialVersionUID =
DeltaScript.VERSION.hashCode();
// Static Tags
// ----------------------------------------------------
public static final DS_Tag
T_JAVA_EXCEPTION = tag("JavaException"),
T_ASSERTION = tag("Assertion"),
T_UNDEFINED = tag("Undefined"),
T_IMMUTABLE = tag("Immutable"),
T_INVALID_TAG = tag("InvalidTag"),
T_INVALID_OP = tag("InvalidOperation"),
T_INVALID_TYPE = tag("InvalidType"),
T_INVALID_ARGS = tag("InvalidArguments"),
T_NOT_CALLABLE = tag("NotCallable"),
T_NOT_ITERABLE = tag("NotIterable"),
T_NOT_INDEXABLE = tag("NotIndexable"),
T_NOT_INTEGER = tag("NotInteger"),
T_NOT_NUMBER = tag("NotNumber"),
T_OUT_OF_BOUNDS = tag("OutOfBounds"),
T_WRONG_VEC_SIZE = tag("WrongVectorDimensions"),
T_BLANK = tag("Blank"),
T_INTERNAL = tag("Internal"),
T_PERM_DENIED = tag("PermissionDenied"),
T_ADD = tag("Add"),
T_SUBTRACT = tag("Subtract"),
T_MULTIPLY = tag("Muliply"),
T_DIVIDE = tag("Divide"),
T_INDEX = tag("Index"),
T_INDEXSET = tag("IndexSet");
// Fields
// ----------------------------------------------------
final DScriptContext context;
final List<DSInstr> instrStackTrace;
final Set<DS_Tag> tags = new HashSet<DS_Tag>();
// Constructors
// ----------------------------------------------------
public DScriptErr(String message, DS_Tag... tags)
{this(message, (DSInstr)null, tags);}
public DScriptErr(String message, Throwable cause, DS_Tag... tags)
{this(message, cause, null, tags);}
public DScriptErr(String message, DSInstr sourceInstr, DS_Tag... tags)
{
super(message);
context = DScriptContext.get();
for (DS_Tag tag : tags) addTag(tag);
instrStackTrace = new ArrayList<DSInstr>();
if (sourceInstr != null)
pushStackInstr(sourceInstr);
}
public DScriptErr(String message, Throwable cause, DSInstr sourceInstr,
DS_Tag... tags)
{
super(message, cause);
context = DScriptContext.get();
for (DS_Tag tag : tags) addTag(tag);
instrStackTrace = new ArrayList<DSInstr>();
if (sourceInstr != null)
pushStackInstr(sourceInstr);
if (cause instanceof DScriptErr)
this.tags.addAll(((DScriptErr)cause).tags);
else if (cause != null)
{
addTag(T_JAVA_EXCEPTION);
Class<?> exClass = cause.getClass();
while (exClass != Exception.class && exClass != Throwable.class)
{
String name = exClass.getSimpleName();
if (name.endsWith("Exception"))
addTag(tag(name.substring(0, name.length()-9)));
else
addTag(tag(name));
exClass = exClass.getSuperclass();
}
}
}
public DScriptContext getContext()
{return context;}
public DSInstr getSourceInstr()
{
if (instrStackTrace.isEmpty())
return null;
return instrStackTrace.get(0);
}
public DSInstr[] getInstrStackTrace()
{return instrStackTrace.toArray(new DSInstr[instrStackTrace.size()]);}
public void pushStackInstr(DSInstr instr)
{instrStackTrace.add(instr);}
public DScriptErr addTag(DS_Tag tag)
{
tags.add(tag);
return this;
}
public boolean hasTag(DS_Tag tag)
{
return tags.contains(tag);
}
// Console Output
// ----------------------------------------------------
@Override public String toString()
{
return StringTemplate.$("DeltaScript Error: {} [{}]", getMessage(),
StringUtil.commaJoin(tags));
}
public static _<String, _<Integer, Integer>> locateInSource(String source,
int pos)
{
int lineNum = 1;
int lineStart = -1;
int lineEnd = source.length();
for (int i=0; i<pos; i++)
if (source.charAt(i) == '\n')
{
lineStart = i;
lineNum++;
}
for (int i=lineStart+1; i<lineEnd; i++)
if (source.charAt(i) == '\n')
{
lineEnd = i;
break;
}
return A._(source.substring(lineStart+1, lineEnd),
A._(lineNum, pos-(lineStart+1)));
}
public static String locatorText(String name, String source,
int start, int len)
{
final _<String, _<Integer, Integer>> res = locateInSource(source,start);
final int line = res._2._1,
pos = res._2._2;
final StringBuilder sb = new StringBuilder(name);
sb.append(", line ");
sb.append(line);
sb.append(":\n");
sb.append(res._1);
sb.append('\n');
for (int i=0; i<pos; i++)
sb.append(' ');
for (int i=0; i<len && i+pos<res._1.length(); i++)
sb.append('^');
return sb.toString();
}
public String getLocatorText(String name, String source)
{
return locatorText(name, source,
getSourceInstr().sourceStart(),
getSourceInstr().sourceLength());
}
// Exception Generators
// ----------------------------------------------------
public static final DScriptErr invalidOperation(String op,
DS_Object obj)
{
return new DScriptErr("Invalid operation \"" + op + "\" for " +
obj.getTypeName() + ".", T_INVALID_OP);
}
public static final DScriptErr invalidOperation(String op,
DS_Object obj1, DS_Object obj2)
{
return new DScriptErr("Invalid operation \"" + op + "\" for " +
obj1.getTypeName() + " and " + obj2.getTypeName() + ".",
T_INVALID_OP);
}
public static final DScriptErr invalidCompare(DS_Object v1,
DS_Object v2)
{
return new DScriptErr("Cannot compare " + v1.getTypeName() +
" and " + v2.getTypeName() + ".", T_INVALID_OP);
}
public static final DScriptErr undefined(String name)
{
return new DScriptErr(name + " is undefined in this scope.",
T_UNDEFINED);
}
public static final DScriptErr undefined(String name,
DS_Object context)
{
return new DScriptErr(name + " is undefined for type \"" +
context.getTypeName() + "\".", T_UNDEFINED);
}
public static final DScriptErr immutable(String name)
{
return new DScriptErr(name + " is read-only in this scope.",
T_IMMUTABLE);
}
public static final DScriptErr immutable(String name,
DS_Object context)
{
return new DScriptErr(name + " is read-only for type \"" +
context.getTypeName() + "\".", T_IMMUTABLE);
}
}