package jfun.yan.xml;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jfun.util.Misc;
import jfun.util.StringUtils;
import jfun.util.dict.Dict;
import jfun.yan.Binder;
import jfun.yan.Component;
import jfun.yan.ComponentBinder;
import jfun.yan.Components;
import jfun.yan.Creator;
import jfun.yan.DelegatingComponent;
import jfun.yan.Dependency;
import jfun.yan.Monad;
import jfun.yan.ParameterBinder;
import jfun.yan.PropertyBinder;
import jfun.yan.Verifiable;
import jfun.yan.lifecycle.DefaultLifecycleManager;
import jfun.yan.util.ReflectionUtil;
import jfun.yan.util.Utils;
import jfun.yan.util.deserializer.Deserializer;
import jfun.yan.util.resource.ResourceLoader;
import jfun.yan.xml.nut.Nut;
import jfun.yan.xml.nut.NutDescriptor;
/**
* This class is responsible for setting up references between
* tags.
* local tag and sequence tag need special treatment.
* <p>
* @author Ben Yu
*
*/
class BodyCompiler extends Constants
implements NutEnvironment, Converter, java.io.Serializable{
private static final Set sys_attributes = MyUtil.getNameSet(
new String[]{
ID, VAR, SINGLETON, /*INITIALIZER, DISPOSER, STARTER, STOPPER,*/
TYPE, AUTOWIRE, SYNCHRONIZED, EAGER_INSTANTIATED, EAGER_INSTANTIATED2
}
);
//attributes allowed on <function>
private static final Set fun_attributes = MyUtil.getNameSet(
new String[]{
ID, PARAMS, SYNCHRONIZED
}
);
private static final Set callcc_attributes = MyUtil.getNameSet(
new String[]{
ID, VAR, SINGLETON, /*INITIALIZER, DISPOSER, STARTER, STOPPER,*/
TYPE, SYNCHRONIZED, EXIT
}
);
private static final Set bare_attributes = MyUtil.getNameSet(new String[]{
ID, VAR
});
private final Map nut_descriptors;
//private final ClassLoader cloader;
//private final DefaultLifecycleManager manager;
//private final File basedir;
private final WiringMode defaults;
//private final Map services;
private final Interpreter interpreter;
private final Set reserves;
private final Object module_id;
private final boolean default_eager_mode;
//private final AutoWiringMap wirings;
public ParameterBinder getParameterWiringMode(String mode_name, Location loc) {
return MyUtil.autocast(MyUtil.getParamWiring(mode_name, getCustomWiringModes(),
loc, defaults.getParameterWiring()),
loc, this);
}
public PropertyBinder getPropertyWiringMode(String mode_name, Location loc) {
return MyUtil.autocast(
MyUtil.getPropWiring(mode_name, getCustomWiringModes(),
loc, defaults.getPropertyWiring()),
loc, this);
}
BodyCompiler(Interpreter interpreter, Object module_id,
Map nut_descriptors, String list_separator, String map_separator,
WiringMode defaults, //AutoWiringMap wirings,
Set reserves, final boolean eager_default) {
this.interpreter = interpreter;
this.module_id = module_id;
//this.cloader = cloader;
// this.manager = manager;
//this.basedir = base;
this.nut_descriptors = nut_descriptors;
this.list_separator = list_separator;
this.map_separator = map_separator;
this.defaults = defaults;
//this.services = services;
this.reserves = reserves;
this.default_eager_mode = eager_default;
//this.wirings = wirings;
}
public Statements compileStatements(
final Tag thistag,
final Dict initial_ctxt,
final IdChecker idchecker,
final String tagname,
List nodes){
//top level components use the global default singleton mode.
final LocalScope scope = compileLocalScope(
tagname, initial_ctxt, nodes, idchecker,
defaults.getSingletonMode(), true);
final Object[] keys = scope.getKeys();
final Stmt[] stmts = scope.getDefinitions();
return new Statements(keys, stmts);
}
private final String list_separator;
private final String map_separator;
private ConfigurationException raiseTypeMismatch(Class type, Object v, Location loc){
throw new ConfigurationException(Misc.getTypeName(type)+" expected, while "
+(v==null?"null":Misc.getTypeName(v.getClass()))
+" encountered.", loc);
}
private Object fromList(Class type, List ll, Location loc)
throws IllegalAccessException, InstantiationException,
InvocationTargetException{
final int sz = ll.size();
if(isListType(type)){
final List result = Utils.createList(type, sz);
result.addAll(ll);
return result;
}
else if(Set.class.isAssignableFrom(type)){
final Set result = Utils.createSet(type, sz);
for(int i=0; i<sz; i++){
final Object elem = ll.get(i);
if(result.contains(elem)){
throw new ConfigurationException("duplicate set element: "+elem, loc);
}
result.add(elem);
}
return result;
}
else if(type.isArray()){
final Class etype = type.getComponentType();
final Object result = Array.newInstance(etype, sz);
for(int i=0; i<sz; i++){
final Object elem = convert(etype, ll.get(i), loc);
Array.set(result, i, elem);
}
return result;
}
return null;
}
private Object cast(Class type, Object obj, Location loc)
throws IllegalAccessException, InstantiationException,
InvocationTargetException{
if(Component.class.equals(type)){
return NutsUtils.asComponent(obj);
}
else if(obj instanceof ListLiteral){
final Object result = fromList(type, (ListLiteral)obj, loc);
if(result != null) return result;
}
else if(obj instanceof MapLiteral){
final MapLiteral ml = (MapLiteral)obj;
final int sz = ml.size();
if(Map.class.isAssignableFrom(type)){
final Map result = Utils.createMap(type, sz);
final Object[] keys = ml.getKeys();
for(int i=0; i<keys.length; i++){
final Object key = keys[i];
final Object val = ml.get(key);
result.put(key, val);
}
return result;
}
}
else if(type.isArray()){
final Class elemtype = type.getComponentType();
return MyUtil.toArray(elemtype, convert(elemtype, obj, loc));
}
else if(List.class.isAssignableFrom(type) || Collection.class.equals(type)){
return MyUtil.toList(type, obj);
}
else if(Set.class.isAssignableFrom(type)){
return MyUtil.toSet(type, obj);
}
throw raiseTypeMismatch(type, obj, loc);
}
public Component cast(final Class target_type, Component c,
final Location loc){
return MyUtil.cast(target_type, c, loc, this);
}
public Object convert(Class expected, Object obj, Location loc){
if(expected==null) return obj;
if(ReflectionUtil.isInstance(expected, obj)){
return obj;
}
else if(String.class.equals(expected)){
if(obj instanceof Literal){
return ((Literal)obj).toText();
}
else return obj.toString();
}
else if(obj instanceof String){
try{
return convertLiteral(expected, (String)obj, loc);
}
catch(Throwable e){
throw raise(e, loc);
}
}
try{
return cast(expected, obj, loc);
}
catch(Throwable e){
throw raise(e, loc);
}
}
private Object convertLiteral(Class type, String str, Location loc)
throws Exception{
if(type.isInstance(str)) return str;
str = str.trim();
if(isCompoundType(type)){
final String[] subs = NutsUtils.split(str, list_separator);
final List sublist = Utils.asList(subs);
final Object result = fromList(type, sublist, loc);
if(result != null) return result;
}
else if(Map.class.isAssignableFrom(type)){
if(!(str.startsWith("{") && str.endsWith("}"))){
throw new IllegalArgumentException("map literal has to be enclosed with {}");
}
str = str.substring(1, str.length()-1);
final String[] entries = NutsUtils.split(str, map_separator);
final Map map = jfun.yan.util.Utils.createMap(type, entries.length);
for(int i=0; i<entries.length; i++){
final String entry = entries[i];
final int delimit = entry.indexOf('=');
if(delimit<0){
throw new IllegalArgumentException("'=' is required for each map entry.");
}
final String key = entry.substring(0, delimit).trim();
if(map.containsKey(key)){
throw new IllegalArgumentException("duplicate map key: "+key);
}
final String val = entry.substring(delimit+1, entry.length());
map.put(key, val);
}
return map;
}
/*
else if(Class.class.equals(type)){
return MyUtil.getClass(getComponentClassLoader(), str);
}
else if(File.class.equals(type)){
return NutsUtils.toFile(getBaseDir(), str);
}
else if(URL.class.equals(type)){
return NutsUtils.toUrl(getBaseDir(), str);
}*/
try{
return deserialize(type, str);
}
catch(Throwable e){
throw raise("failed to convert string to "+Misc.getTypeName(type), e, loc);
}
}
private String getIdAttribute(Tag tag){
return getWord(tag, ID);
}
private String getVarAttribute(Tag tag){
return getWord(tag, VAR);
}
private void checkWord(String word, String kind, Location loc){
if(word!=null && !NutsUtils.isValidId(word)){
throw new ConfigurationException("invalid "+kind, loc);
}
if(reserves.contains(word)){
throw new ConfigurationException(kind+" "+word+" is a reserved word.",
loc);
}
}
private String getWord(Tag tag, String kind){
final String word = tag.getAttribute(kind);
checkWord(word, kind, tag.getLocation());
return word;
}
private String getMandatoryVar(Tag tag){
final String var = getVarAttribute(tag);
if(var==null)
throw new ConfigurationException("missing mandatory "+VAR,
tag.getLocation());
return var;
}
private static Dict declareName(Dict ctxt, String name, Location loc){
return ctxt.put(name, new Bound(name, loc));
}
static class CurrentLocation extends ThreadLocal{
public Location getLocation(){
return (Location)this.get();
}
public void setLocation(Location loc){
this.set(loc);
}
}
static CurrentLocation current_location = new CurrentLocation();
private final class TagCompiler{
private final int ind;
private final Dict ctxt;
private final Tag tag;
private final SingletonMode singleton_mode;
private final boolean var_permitted;
private final boolean is_global;
TagCompiler(int ind, Dict ctxt, Tag tag,
SingletonMode singleton_mode, boolean var_permitted,
boolean is_global) {
this.ind = ind;
this.ctxt = ctxt;
this.tag = tag;
this.singleton_mode = singleton_mode;
this.var_permitted = var_permitted;
this.is_global = is_global;
}
public Location getLocation(){
return tag.getLocation();
}
public String getName(){
return tag.getName();
}
private String getErrorMessage(String tagname, String msg){
return "<"+tagname+"> - "+msg;
}
private ConfigurationException raise(String msg){
throw new ConfigurationException(getErrorMessage(getName(), msg), tag.getLocation());
}
private ConfigurationException raise(Throwable e){
if(e instanceof ConfigurationException){
throw (ConfigurationException)e;
}
else if(e instanceof InvocationTargetException){
return raise(((InvocationTargetException)e).getTargetException());
}
throw new ConfigurationException(getErrorMessage(getName(), e.getMessage()), e, tag.getLocation());
}
private ConfigurationException raise(Throwable e, Location loc){
return BodyCompiler.raise(e, loc);
}
public String getAttribute(String name){
return tag.getAttribute(name);
}
private void assertAttributes(Set table){
MyUtil.assertAttributes(tag, table);
}
private Dict declareNames(Dict ctxt, String[] names){
final Stmt[] stmts = new Stmt[names.length];
final Set cache = new HashSet(names.length);
for(int i=0; i<names.length; i++){
final String name = names[i];
if(cache.contains(name)){
throw raise("duplicate parameter name: "+name);
}
cache.add(name);
checkWord(name, "parameter name", getLocation());
stmts[i] = new Bound(names[i], getLocation());
}
return ctxt.puts(names, stmts);
}
private Stmt compile(){
current_location.setLocation(getLocation());
final List subnodes = tag.getSubNodes();
Dict ctxt = this.ctxt;
if(BINDER.equals(tag.getName())){
final String varname = getMandatoryVar(this.tag);
ctxt = declareName(ctxt, varname, getLocation());
}
else if(FUNCTION.equals(tag.getName())){
final String paramnames = MyUtil.getMandatory(tag, PARAMS)
.trim();
final String[] params = NutsUtils.split(paramnames, list_separator);
/*if(params.length==0){
throw raise("at least one parameter needs to be specified.");
}*/
ctxt = declareNames(ctxt, params);
}
if(subnodes.isEmpty()){
return compileMe(ctxt, subnodes);
}
else{
final Node node = (Node)subnodes.get(0);
if(node instanceof Tag){
final Tag t = (Tag)node;
if(LOCAL.equals(t.getName())){
final LocalScope scope = compileLocalScope(ctxt, t);
return withScope(scope, compileMe(scope.getScope(),
subnodes.subList(1, subnodes.size())), getLocation());
}
}
return compileMe(ctxt, subnodes);
}
}
private Stmt wrapStmt(final Dict local_ctxt, final Stmt stmt){
//final String typename = getAttribute(attrs, TYPE);
final Stmt type = getTypeAttribute(local_ctxt);
final Stmt wiring = getAutowireAttribute(local_ctxt, defaults.getParameterWiring());
final Stmt singleton_mode = getSingletonAttribute(local_ctxt, this.singleton_mode);
final Stmt sync_stmt = getSynchronizedAttribute(local_ctxt);
final Stmt eager = is_global?getEagerInstantiationAttribute(local_ctxt):null;
final String id = getAttribute(ID);
final Location loc = stmt.getLocation();
return new Stmt(){
public String toString(){
return stmt.toString();
}
public Object run(Dict frame, Runtime runtime){
Object obj = stmt.run(frame, runtime);
final Class casttype =
type==null?null:(Class)type.run(frame, runtime);
final ParameterBinder autowire =
wiring==null?null:(ParameterBinder)wiring.run(frame, runtime);
final SingletonMode singleton =
singleton_mode==null?null:
(SingletonMode)singleton_mode.run(frame, runtime);
final boolean sync = sync_stmt==null?false:
((Boolean)sync_stmt.run(frame, runtime)).booleanValue();
if(obj instanceof Creator){
final Component result = MyUtil.wrapComponent(
Components.adapt((Creator)obj), runtime, loc,
casttype, singleton, autowire, sync, BodyCompiler.this);
if(eager!=null){
final boolean eagerly_instantiated =
((Boolean)eager.run(frame, runtime)).booleanValue();
if(eagerly_instantiated){
registerEagerInstantiation(ind, id, result);
}
}
return result;
}
else{
if(sync && obj instanceof NutsFunction){
obj = new SynchronizedNutsFunction((NutsFunction)obj);
}
if(casttype != null){
try{
return convert(casttype, obj, loc);
}
catch(Throwable e){
throw raise(e, loc);
}
}
else return obj;
}
}
public Location getLocation(){
return loc;
}
public Class getType(){
return stmt.getType();
}
};
}
private Stmt compileMe(Dict local_ctxt, List nodes){
return wrapStmt(local_ctxt, compileTagBody(local_ctxt, nodes));
}
private Stmt compileTagBody(Dict local_ctxt, List nodes){
final String name = tag.getName();
//final Location loc = tag.getLocation();
if(SEQUENCE.equals(name)){
return compileSequence(local_ctxt, nodes);
}
else if(BINDER.equals(name)){
return compileBinder(local_ctxt, nodes);
}
else if(FUNCTION.equals(name)){
return compileFunction(local_ctxt, nodes);
}
else if(CALLCC.equals(name)){
return compileCallcc(local_ctxt, nodes);
}
else{
final NutDescriptor desc = (NutDescriptor)nut_descriptors.get(name);
if(desc == null){
throw raise("unknown nut");
}
return compileNut(name, desc, local_ctxt, nodes, false);
}
}
private Stmt compileFunction(Dict ctxt, List nodes){
assertAttributes(fun_attributes);
final int sz = nodes.size();
if(sz==0){
throw raise("empty function not allowed.");
}
else if(sz>1){
throw raise("only one sub-element is allowed.");
}
final String[] params = StringUtils.split(
MyUtil.getMandatory(tag, PARAMS),
list_separator);
final String fname = getIdAttribute(tag);
final String funname = fname==null?"\\":fname;
//we do not call compileSequential because <function> is not a Component only tag.
//like <value>, it returns anything.
final Stmt body = //compileSequential(ctxt, nodes);
compileNode(0, ctxt, (Node)nodes.get(0), false);
final Location loc = getLocation();
return new Stmt(){
public Class getType() {
return NutsFunction.class;
}
public Object run(final Dict frame, final Runtime runtime) {
return new NutsFunction(){
public Class getReturnType(){
return body.getType();
}
public Object call(Object[] args) {
return body.run(frame.puts(params, args), runtime);
}
public String getName() {
return funname+StringUtils.listArray("(",",",")",params);
}
public int getParameterCount() {
return params.length;
}
public String[] getParameterNames(){
return (String[])params.clone();
}
public Location getLocation() {
return loc;
}
public String toString() {
return funname;
}
};
}
public Location getLocation() {
return loc;
}
public String toString(){
return funname+StringUtils.listArray("(",",",")",params);
}
};
}
private SideEffect property_setter(final PropertyDescriptor prop,
final Stmt stmt){
return dyn_method_invoker(prop.getWriteMethod(), prop.getPropertyType(), stmt);
}
private SideEffect literal_property_setter(final PropertyDescriptor prop,
final String val){
try{
return method_invoker(prop.getWriteMethod(),
prop.getPropertyType(), val, getLocation());
}
catch(Throwable e){
throw raise(e);
}
}
private SideEffect method_invoker(final Method mtd,
final Class param_type, final String val, final Location loc){
return new SideEffect(){
public void apply(Object obj, Dict ctxt, Runtime runtime){
try{
mtd.invoke(obj, new Object[]{convertLiteral(param_type, val, loc)});
}
catch(Throwable e){
throw raise(e);
}
}
public String toString(){
return mtd.getName()+"("+val+")";
}
};
}
private Stmt compileNut(final String nutname,final NutDescriptor desc,
boolean is_subnut){
final List subnodes = tag.getSubNodes();
if(subnodes.isEmpty()){
return compileNut(nutname, desc, ctxt, subnodes, is_subnut);
}
else{
final Node node = (Node)subnodes.get(0);
if(node instanceof Tag){
final Tag t = (Tag)node;
if(LOCAL.equals(t.getName())){
final LocalScope scope = compileLocalScope(ctxt, t);
final Dict nctxt = scope.getScope();
return withScope(scope, compileNut(nutname, desc, nctxt,
subnodes.subList(1, subnodes.size()), is_subnut), getLocation());
}
}
return compileNut(nutname, desc, ctxt, subnodes, is_subnut);
}
}
/**
* To convert a literal directly to a target type.
* @param expected_type the expected type.
* @param local_ctxt the current context.
* @param literal the literal.
* @return the target object.
*/
private Stmt compileLiteralValue(Class expected_type,
Dict local_ctxt, String literal){
if(literal==null) return null;
if(MyUtil.isRefName(literal)){
final Stmt stmt = compileRefSymbol(literal, local_ctxt);
return cast(expected_type, stmt);
}
else{
final Location loc = getLocation();
final Object literal_val = StringInterpolator.interpolate(literal, local_ctxt, loc);
if(literal_val instanceof Stmt){
return cast(expected_type, (Stmt)literal_val);
}
else return MyUtil.typedValue(expected_type, literal_val, loc, BodyCompiler.this);
}
}
/**
* To convert a literal to a string (when it is a ref) and convert it using a
* FromLiteral object.
* @param type the final target type.
* @param literal the literal.
* @param fl the FromLiteral object.
* @param local_ctxt the current context.
* @param loc the location
* @return the Stmt object.
*/
private Stmt compileLiteralString(final Class type, String literal,
final FromLiteral fl,
Dict local_ctxt, final Location loc){
if(MyUtil.isRefName(literal)){
final Stmt stmt = compileRefSymbol(literal, local_ctxt);
return transformLiteralString(fl, type, stmt, loc);
}
else{
final Object literal_val = StringInterpolator.interpolate(literal, local_ctxt, loc);
if(literal_val instanceof Stmt){
return transformLiteralString(fl, type, (Stmt)literal_val, loc);
}
else{
return MyUtil.value(fl.fromLiteral(literal_val.toString()), loc);
}
}
}
private Stmt transformLiteralString(final FromLiteral fl,
final Class type,
final Stmt stmt, final Location loc){
return new Stmt(){
public Class getType() {
return type;
}
public Object run(Dict frame, Runtime runtime) {
final String val = (String)convert(String.class,
stmt.run(frame, runtime), loc);
return fl.fromLiteral(val);
}
public Location getLocation() {
return loc;
}
public String toString() {
return stmt.toString();
}
};
}
private Stmt compileAttributeValue(Class expected_type,
Dict local_ctxt, String name){
final String literal = getAttribute(name);
return compileLiteralValue(expected_type, local_ctxt, literal);
}
private Stmt getBooleanAttribute(Dict local_ctxt, String name, boolean def){
final Stmt result = compileAttributeValue(boolean.class,
local_ctxt, name);
if(result==null){
return MyUtil.value(Boolean.valueOf(def), getLocation());
}
else return result;
}
private Stmt getSynchronizedAttribute(Dict local_ctxt){
return getBooleanAttribute(local_ctxt, SYNCHRONIZED, false);
}
private Stmt getEagerInstantiationAttribute(Dict local_ctxt){
Stmt result = compileLiteralValue(boolean.class, local_ctxt,
MyUtil.getEagerMode(tag));
if(result==null){
return MyUtil.value(Boolean.valueOf(default_eager_mode), getLocation());
}
else return result;
}
private Stmt getTypeAttribute(Dict local_ctxt){
final String literal = getAttribute(TYPE);
if(literal==null) return null;
final Location loc = getLocation();
final FromLiteral fl = new FromLiteral(){
public Object fromLiteral(String text){
try{
return MyUtil.getClass(getComponentClassLoader(), text);
}
catch(ClassNotFoundException e){
throw new ConfigurationException(
getErrorMessage(tag.getName(),
"class "+text+" not found."), e,
loc);
}
}
};
return compileLiteralString(Class.class, literal, fl, local_ctxt, loc);
/*
if(MyUtil.isRefName(literal)){
final Stmt stmt = compileRefSymbol(literal, local_ctxt);
return cast(Class.class, stmt);
}
else{
try{
return MyUtil.value(MyUtil.getClass(getComponentClassLoader(), literal),
getLocation());
}
catch(ClassNotFoundException e){
throw new ConfigurationException(
getErrorMessage(tag.getName(),
"class "+literal+" not found."), e,
getLocation());
}
}*/
}
private Stmt getAutowireAttribute(Dict local_ctxt, final ParameterBinder def){
final String literal = getAttribute(AUTOWIRE);
final Location loc = getLocation();
if(literal==null) return MyUtil.value(def, loc);
final FromLiteral fl = new FromLiteral(){
public Object fromLiteral(String autowire){
return MyUtil.getParamWiring(autowire,
getCustomWiringModes(), loc, def);
}
};
return compileLiteralString(ParameterBinder.class, literal, fl, local_ctxt, loc);
/*
if(MyUtil.isRefName(literal)){
final Stmt stmt = compileRefSymbol(literal, local_ctxt);
return new Stmt(){
public Class getType() {
return ParameterBinder.class;
}
public Object run(Dict frame, Runtime runtime) {
final String autowire = (String)convert(String.class,
stmt.run(frame, runtime), loc);
return MyUtil.getParamWiring(autowire,
getCustomWiringModes(), loc, def);
}
public Location getLocation() {
return loc;
}
public String toString() {
return stmt.toString();
}
};
}
else{
return MyUtil.value(MyUtil.getParamWiring(literal,
getCustomWiringModes(), loc, def), loc);
}*/
}
private Stmt getSingletonAttribute(Dict local_ctxt, final SingletonMode def){
final String literal = getAttribute(SINGLETON);
final Location loc = getLocation();
if(literal==null)
return MyUtil.value(def, loc);
final FromLiteral fl = new FromLiteral(){
public Object fromLiteral(String singleton){
return MyUtil.getSingletonStrategy(singleton, loc, def);
}
};
return compileLiteralString(ComponentDecorator.class, literal, fl, local_ctxt, loc);
/*
if(literal==null) return MyUtil.value(def, loc);
if(MyUtil.isRefName(literal)){
final Stmt stmt = compileRefSymbol(literal, local_ctxt);
return new Stmt(){
public Class getType() {
return ComponentDecorator.class;
}
public Object run(Dict frame, Runtime runtime) {
final String singleton = (String)convert(String.class,
stmt.run(frame, runtime), loc);
return MyUtil.getSingletonStrategy(singleton, loc, def);
}
public Location getLocation() {
return loc;
}
public String toString() {
return stmt.toString();
}
};
}
else{
return MyUtil.value(MyUtil.getSingletonStrategy(literal, loc, def), loc);
}*/
}
private Stmt compileRefSymbol(String refname, Dict local_ctxt){
return compileRef(refname.substring(1), local_ctxt);
}
private Stmt compileRef(String refname, Dict local_ctxt){
if(!NutsUtils.isValidId(refname)){
throw raise("invalid identifier: "+refname);
}
if(!local_ctxt.containsKey(refname)){
throw raise("reference not recognized: "+refname);
}
return new Bound(refname, getLocation());
}
private Stmt compileLiteral(String val, Dict local_ctxt){
if(MyUtil.isRefName(val)){
//a reference.`
return compileRefSymbol(val, local_ctxt);
}
else{
final Location loc = getLocation();
return asStmt(StringInterpolator.interpolate(val, local_ctxt, loc), loc);
//MyUtil.value(val, getLocation());
}
}
private Object compileRefLiteral(final Class type, String val, Dict local_ctxt){
if(MyUtil.isRefName(val)){
//a reference.`
return compileRefSymbol(val, local_ctxt);
}
else if(isRefType(type)){
//we expect Component, but a string is passed in,
//this is very probably that the string is a ref name.
return compileRef(val, local_ctxt);
}
else return StringInterpolator.interpolate(val, local_ctxt, getLocation());
//return null;
}
private Stmt compileLiteral(final Class type, String val, Dict local_ctxt){
return asStmt(compileRefLiteral(type, val, local_ctxt), getLocation());
/*
final Stmt ret = compileRefLiteral(type, val, local_ctxt);
if(ret==null)
return MyUtil.value(val, getLocation());
else return ret;*/
}
public Entry[] compileMapEntries(String str, Dict local_ctxt){
str = str.trim();
final String[] entry_strs = NutsUtils.split(str, map_separator);
final ArrayList entries = new ArrayList(entry_strs.length);
for(int i=0; i<entry_strs.length; i++){
entries.add(compileMapEntry(entry_strs[i], local_ctxt));
}
final Entry[] result = new Entry[entries.size()];
entries.toArray(result);
return result;
}
private Entry compileMapEntry(String str, Dict local_ctxt){
final int delimit = str.indexOf('=');
if(delimit<0){
throw raise("'=' is missing in a map entry.");
}
final String key_str = str.substring(0, delimit).trim();
final String val_str = str.substring(delimit+1, str.length()).trim();
final Stmt key = compileLiteral(key_str, local_ctxt);
final Stmt val = compileLiteral(val_str, local_ctxt);
return new Entry(key, val);
}
private Stmt[] compileParts(String[] parts, Class type, Dict local_ctxt){
final Stmt[] result = new Stmt[parts.length];
for(int i=0; i<result.length; i++){
result[i] = compileLiteral(type, parts[i], local_ctxt);
}
return result;
}
private Stmt[] compileParts(String[] parts, Dict local_ctxt){
final Stmt[] result = new Stmt[parts.length];
for(int i=0; i<result.length; i++){
result[i] = compileLiteral(parts[i], local_ctxt);
}
return result;
}
private Stmt compileListLiteral(final String val, Dict local_ctxt){
final String[] parts = NutsUtils.split(val, list_separator);
final Location loc = getLocation();
final Stmt[] stmts = compileParts(parts, local_ctxt);
return new LiteralStmt(ArrayList.class, val, loc){
public Object run(Dict frame, Runtime runtime) {
final ListLiteral ll = new ListLiteral(stmts.length);
for(int i=0; i<stmts.length; i++){
final Stmt stmt = stmts[i];
ll.add(stmt.run(frame, runtime));
}
return ll;
}
};
}
private Stmt compileMapLiteral(final String val, Dict local_ctxt){
final Entry[] entries = compileMapEntries(val, local_ctxt);
final Location loc = getLocation();
return new LiteralStmt(ArrayList.class, val, loc){
public Object run(Dict frame, Runtime runtime) {
final MapLiteral ml = new MapLiteral(entries.length);
for(int i=0; i<entries.length; i++){
final Entry entry = entries[i];
final Object key = ((Stmt)entry.getKey()).run(frame, runtime);
final Object val = ((Stmt)entry.getVal()).run(frame, runtime);
ml.build(key, val);
}
return ml;
}
};
}
private Stmt compileCompound(final String val, final Class type,
Dict local_ctxt){
final String[] parts = NutsUtils.split(val, list_separator);
final Location loc = getLocation();
if(type.isArray()){
final Class etype = type.getComponentType();
final Stmt[] stmts = compileParts(parts, type,
local_ctxt);
return new LiteralStmt(type, val, loc){
public Object run(Dict frame, Runtime runtime) {
final Object arr = Array.newInstance(etype, stmts.length);
for(int i=0; i<stmts.length; i++){
final Stmt stmt = stmts[i];
Array.set(arr, i, convert(etype, stmt.run(frame, runtime), stmt.getLocation()));
}
return arr;
}
};
}
else if(isListType(type)){
final Stmt[] stmts = compileParts(parts, local_ctxt);
return new LiteralStmt(type, val, loc){
public Object run(Dict frame, Runtime runtime) {
try{
final List list = jfun.yan.util.Utils
.createList(type, stmts.length);
for(int i=0; i<stmts.length; i++){
list.add(stmts[i].run(frame, runtime));
}
return list;
}
catch(Throwable e){
throw raise(e);
}
}
};
}
else if(Set.class.isAssignableFrom(type)){
final Stmt[] stmts = compileParts(parts, local_ctxt);
return new LiteralStmt(type, val, loc){
public Object run(Dict frame, Runtime runtime) {
try{
final Set set = jfun.yan.util.Utils
.createSet(type, stmts.length);
for(int i=0; i<stmts.length; i++){
final Object obj = stmts[i].run(frame, runtime);
if(set.contains(obj)){
throw raise("duplicate set element:"+obj);
}
set.add(obj);
}
return set;
}
catch(Throwable e){
throw raise(e);
}
}
};
}
else{
throw raise("either list or array is expected, "+Misc.getTypeName(type)
+" encountered");
}
}
private Stmt compileMapLiteral(String val, Class type, Dict local_ctxt){
final Entry[] entries = compileMapEntries(val, local_ctxt);
return new LiteralStmt(type, val, getLocation()){
public Object run(Dict frame, Runtime runtime) {
try{
final Map map = jfun.yan.util.Utils
.createMap(type, entries.length);
for(int i=0; i<entries.length; i++){
final Entry entry = entries[i];
final Object key = ((Stmt)entry.getKey())
.run(frame, runtime);
if(map.containsKey(key)){
throw raise("duplicate map key:"+key);
}
final Object val = ((Stmt)entry.getVal())
.run(frame, runtime);
map.put(key, val);
}
return map;
}
catch(Throwable e){
throw raise(e);
}
}
};
}
private boolean isMapLiteral(String val){
if(val.startsWith("{")){
//map literal.
if(!val.endsWith("}")){
throw raise("map literal has to be ended by a '}'");
}
return true;
}
else
return false;
}
private boolean isListLiteral(String val){
return (val.indexOf(',')>=0 && val.indexOf('$')>=0);
}
private Stmt attemptMapLiteral(String val, Dict local_ctxt){
val = val.trim();
if(isMapLiteral(val)){
//map literal.
return compileMapLiteral(val.substring(1, val.length()-1), local_ctxt);
}
else return null;
}
private Stmt attemptListLiteral(String val, Dict local_ctxt){
if(isListLiteral(val)){
return compileListLiteral(val, local_ctxt);
}
else return null;
}
private Stmt attemptMap(Class type, String val, Dict local_ctxt){
val = val.trim();
if(isMapLiteral(val)){
return compileMapLiteral(val.substring(1, val.length()-1), type, local_ctxt);
}
else return null;
}
private Stmt attemptCompound(Class type, String val, Dict local_ctxt){
if(isListLiteral(val)){
//we only care about list/array of length 2 or more.
//for a ref string with no ',', we don't treat it as array/list.
return compileCompound(val, type, local_ctxt);
}
else return null;
}
private SideEffect compileNutAttribute(String val,
final PropertyDescriptor prop, Dict local_ctxt){
final Class type = prop.getPropertyType();
if(Map.class.isAssignableFrom(type)){
final Stmt stmt = attemptMap(type, val, local_ctxt);
if(stmt!=null)
return property_setter(prop, stmt);
}
else if(isCompoundType(type)){
final Stmt stmt = attemptCompound(type, val, local_ctxt);
if(stmt!=null)
return property_setter(prop, stmt);
}
else if(Object.class.equals(type)){
//wild guess, populate a Literal.
Stmt stmt = attemptMapLiteral(val, local_ctxt);
if(stmt==null)
stmt = attemptListLiteral(val, local_ctxt);
if(stmt!=null)
return property_setter(prop, stmt);
}
return compileTerm(type, val, prop, local_ctxt);
}
private SideEffect compileTerm(final Class type, String val, final PropertyDescriptor prop, Dict local_ctxt) {
final Object result = compileRefLiteral(type, val, local_ctxt);
if(result instanceof Stmt){
return property_setter(prop, (Stmt)result);
}
else{
return literal_property_setter(prop, (String)result);
}
/*
final Object result = compileRefLiteral(type, val, local_ctxt);
if(ref==null)
return literal_property_setter(prop, val);
else return property_setter(prop, ref);*/
}
Object returnSubNut(NutDescriptor desc, Object nut, Object result){
if(result==null || !ReflectionUtil.isInstance(desc.getType(), result)){
return nut;
}
else return result;
}
private Stmt getCollectionNut(final String nutname,
final NutDescriptor desc, final ArrayList mutations,
final Stmt[] args, final boolean is_subnut){
final Class elem_type = desc.getSetterElementType();
final Location loc = getLocation();
return new Stmt(){
public String toString(){
return nutname;
}
public Object run(Dict frame, Runtime runtime){
final Object nut = createNut(nutname,
desc, mutations, frame, runtime);
try{
if(args.length!=0){
final Object arr = Array.newInstance(elem_type, args.length);
for(int i=0; i<args.length; i++){
final Object elem = args[i].run(frame, runtime);
final Object converted = convert(elem_type, elem, loc);
try{
Array.set(arr, i, converted);
}
catch(IllegalArgumentException e){
raise("array element type mismatch: ["+i+"]");
}
}
desc.getSetter().invoke(nut, new Object[]{arr});
}
final Object result = desc.getEvaluator().eval(nut);
//for subnut, we always return the nut instance because that's what the enclosing tag is expecting.
return is_subnut?returnSubNut(desc, nut, result):result;
}
catch(Throwable e){
throw raise(e);
}
}
public Class getType(){
return is_subnut?desc.getType():desc.getEvaluator().getType();
}
public Location getLocation(){
return loc;
}
};
}
private void populateAdders(final String nutname, final NutDescriptor desc,
final List nodes, final List mutations, final Dict ctxt){
final int sz = nodes.size();
for(int i=0; i<sz; i++){
final Node node = (Node)nodes.get(i);
if(node instanceof CharacterData){
final CharacterData literal = (CharacterData)node;
populateLiteralAdder(nutname, desc, literal, mutations);
}
else{
final Tag subtag = (Tag)node;
populateSubnutAdder(i, desc, subtag, mutations, ctxt);
}
}
}
private void populateLiteralAdder(final String nutname, final NutDescriptor desc,
CharacterData literal, List mutations){
final Location subloc = literal.getLocation();
/*
final Method adder = desc.getAdder("");
if(adder==null){
if(literal.getText().trim().length()==0){
//ignore whitespaces.
return;
}
throw new ConfigurationException("literal disallowed within "+nutname, subloc);
}*/
if(literal.getText().trim().length()==0){
//ignore whitespaces.
return;
}
try{
/*
mutations.add(method_invoker(adder,
convertLiteral(desc.getAdderType(""), literal.getText(),
getLocation())));
*/
mutations.add(anonymous_adder_invoker(desc,
MyUtil.value(literal.getText(), literal.getLocation())));
}
catch(Throwable e){
raise(e, subloc);
}
}
private void populateSubnutAdder(int seq, final NutDescriptor desc,
Tag subtag, List mutations, Dict ctxt){
final String subname = subtag.getName();
NutDescriptor subdesc = desc.getSubDescriptor(subname);
if(subdesc!=null){
final NutDescriptor customized_desc =
(NutDescriptor)nut_descriptors.get(subname);
if(customized_desc!=null){
if(subdesc.getType().isAssignableFrom(customized_desc.getType())){
//we have a customized nut tag that's subtype.
subdesc = customized_desc;
}
}
final Stmt sub = new TagCompiler(seq, ctxt, subtag, null, false, false)
.compileNut(subname, subdesc, true);
final Method adder = desc.getAdder(subname);
mutations.add(dyn_method_invoker(adder, desc.getAdderType(subname), sub));
}
else{
//try anonymous adder if the name is a top-level nut
/*subdesc = (NutDescriptor)nut_descriptors.get(subname);
if(subdesc == null){
throw new ConfigurationException("unknown subnut: "+subname,
subtag.getLocation());
}*/
final Stmt sub = compileLocalTag(seq, ctxt, subtag, false);
mutations.add(anonymous_adder_invoker(desc, sub));
}
}
private Stmt getRegularNut(final String nutname, final NutDescriptor desc,
final ArrayList mutations, final boolean is_subnut){
final Location loc = getLocation();
return new Stmt(){
public String toString(){
return nutname;
}
public Object run(Dict frame, Runtime runtime){
final Object nut = createNut(nutname, desc, mutations,
frame, runtime);
try{
final Object result = desc.getEvaluator().eval(nut);
return is_subnut?returnSubNut(desc, nut, result):result;
}
catch(Throwable e){
throw raise(e);
}
}
public Class getType(){
return is_subnut?desc.getType():desc.getEvaluator().getType();
}
public Location getLocation(){
return loc;
}
};
}
private Object createNut(final String nutname,
NutDescriptor desc, ArrayList mutations,
Dict ctxt, Runtime runtime){
try{
final Object nut = desc.createNut();
if(nut instanceof Nut){
final Nut r = (Nut)nut;
r.initGloballyDefined(is_global);
r.initSequenceNumber(ind);
r.initTagLocation(getLocation());
r.initNutEnvironment(BodyCompiler.this);
r.initTagName(nutname);
}
final int sz = mutations.size();
for(int i=0; i<sz; i++){
((SideEffect)mutations.get(i)).apply(nut, ctxt, runtime);
}
return nut;
}
catch(Throwable e){
throw raise(e);
}
}
private boolean isSkippable(String name){
if(sys_attributes.contains(name)){
return true;
}
if(is_global && (EAGER_INSTANTIATED.equals(name)||EAGER_INSTANTIATED2.equals(name))){
return true;
}
else return false;
}
private Stmt compileNut(final String nutname,
final NutDescriptor desc, final Dict local_ctxt, final List nodes,
boolean is_subnut){
final Attributes attrs = tag.getAttributes();
final Map props = desc.getPropertyDescriptors();
final Location loc = getLocation();
//final HashMap propvals = new HashMap();
final ArrayList mutations = new ArrayList();
for(int i=0; i<attrs.size(); i++){
final String name = NutsUtils.canonicalizeAttributeName(attrs.getKey(i));
final String val = attrs.getVal(i);
final PropertyDescriptor prop = (PropertyDescriptor)props.get(name);
if(prop == null){
if(!var_permitted && VAR.equals(name)){
throw raise(VAR + " not permitted in this context.");
}
if(isSkippable(name)){
//sys attribute, we skip.
continue;
}
else{
throw new ConfigurationException(name + " attribute not supported", loc);
}
}
//compileNutAttribute(val, prop, ctxt, mutations);
mutations.add(compileNutAttribute(val, prop, local_ctxt));
}
if(desc.isCollectionNut()){
//sub-elements are an array.
final Stmt[] stmts = new Stmt[nodes.size()];
final Class elem_type = desc.getSetterElementType();
for(int i=0; i<stmts.length; i++){
final Stmt stmt = compileNode(i, local_ctxt,
(Node)nodes.get(i), false);
checkElementType(elem_type, stmt);
stmts[i] = stmt;
}
return getCollectionNut(nutname, desc, mutations, stmts, is_subnut);
}
else{
populateAdders(nutname, desc, nodes, mutations, local_ctxt);
return getRegularNut(nutname, desc, mutations, is_subnut);
}
}
private void assertNoCustomAttributes(){
if(!var_permitted){
if(tag.getAttribute(VAR)!=null){
raise(VAR + " not permitted in this context.");
}
}
assertAttributes(sys_attributes);
}
private Definition[] compileDefinitions(Dict ctxt, List subnodes,
HashMap local_names){
final Definition[] stmts = new Definition[subnodes.size()];
for(int i=0; i<subnodes.size(); i++){
final Node node = (Node)(subnodes).get(i);
final Location loc = node.getLocation();
if(node instanceof Tag){
Tag sub = (Tag)node;
final String id = getIdAttribute(sub);
final String var = getVarAttribute(sub);
if(id!=null&&var!=null&&id.equals(var)){
throw new ConfigurationException("id and var cannot share the same value",
loc);
}
checkDup(local_names, id, ID, loc);
checkDup(local_names, var, VAR, loc);
final Stmt stmt = compileLocalTag(i, ctxt, sub, true);
//let's allow non-component sub-elements.
/*
if(!maybeType(Component.class, stmt.getType())){
throw new ConfigurationException(getName() + " only allow sub-elements that evaluates to Component",
loc);
}*/
stmts[i] = new Definition(sub.getName(), id, var, stmt);
if(id != null){
ctxt = declareName(ctxt, id, loc);
}
if(var != null)
ctxt = declareName(ctxt, var, loc);
}
else{
throw new ConfigurationException(getName() + " only allow sub-elements that evaluates to Component",
loc);
}
}
return stmts;
}
private Stmt compileCallcc(Dict local_ctxt, List subnodes){
assertAttributes(callcc_attributes);
final String exit = MyUtil.getMandatory(tag, EXIT);
final Location loc = getLocation();
final Stmt body = compileSequential(declareName(local_ctxt, exit, loc),
subnodes);
return new Stmt(){
public Location getLocation() {
return loc;
}
public Class getType() {
return Component.class;
}
public Object run(Dict frame, Runtime runtime) {
final Object id = new ReferentialId(exit);
final NutsContinuation cont = new NutsContinuation(exit, loc, id);
frame = frame.put(exit, cont);
final Object r = body.run(frame, runtime);
if(r instanceof Component){
final Component c = (Component)r;
return new DelegatingComponent(c){
public Object create(Dependency dep){
try{
return super.create(dep);
}
catch(RuntimeException e){
final ContinuationEscapeException escape =
MyUtil.getEscapeException(e);
if(escape!=null && escape.getId()==id){
return escape.getResult();
}
throw e;
}
}
};
/*
return c.recover(new Recovery(){
public Creator recover(RuntimeException e){
final ContinuationEscapeException escape =
MyUtil.getEscapeException(e);
if(escape!=null && escape.getId()==id){
return NutsUtils.asComponent(escape.getResult());
}
throw e;
}
public String toString(){
return c.toString();
}
});*/
}
else return r;
}
};
}
private Stmt compileSequence(Dict local_ctxt, List subnodes){
//id, var and local are already handled.
assertNoCustomAttributes();
return compileSequential(local_ctxt, subnodes);
}
private Stmt compileSequential(Dict local_ctxt, List subnodes){
final HashMap local_names = new HashMap();
final Definition[] stmt = compileDefinitions(local_ctxt, subnodes, local_names);
return bindStatements(stmt, 0, stmt.length);
}
private Stmt compileBinder(Dict ctxt, List subnodes){
assertAttributes(bare_attributes);
final String varname = getMandatoryVar(tag);
final String id = getIdAttribute(tag);
if(id!=null && id.equals(varname)){
throw raise(ID+" and "+VAR+" cannot share the same value");
}
final HashMap local_names = new HashMap();
local_names.put(varname, varname);
final Dict nctxt = ctxt;//declareName(ctxt, varname, getLocation());
final Definition[] stmt = compileDefinitions(nctxt, subnodes, local_names);
return compileBinder(varname, stmt, 0, stmt.length);
}
private Component seq(Component c1, Component c2){
if(c1==null) return c2;
else return c1.seq(c2);
}
private Component evalDefinitions(final Definition[] defs,
final int begin, final int end, Dict frame, Runtime runtime) {
if(begin==end)
return Components.value(null);
Component cc = null;
final int last = end - 1;
for(int i=begin;i<end;i++){
final Definition def = defs[i];
final Object retval = evalStmt(def.stmt, runtime, frame);
final boolean isComponent = retval instanceof Component;
if(isComponent){
cc = seq(cc,(Component)retval);
if(i==last) return cc;
}
else if(i==last){
return seq(cc, NutsUtils.asComponent(retval));
}
/*
if(isComponent){
final Component cur = (Component)retval;
cc = cc==null?cur:cc.seq(cur);
}
else{
//relax this restriction because sometimes we may have a tag
//that may or may not result in a component.
if(i==last){
cc = NutsUtils.asComponent(retval);
}
}*/
frame = nextFrame(def, frame, retval);
final String vi = def.var;
if(vi != null){
if(isComponent){
final Stmt binder = bindStatements(defs, i+1, end);
return cc.bind(evalBinder(vi,
binder, runtime, frame));
}
else{
frame = frame.put(vi, retval);
}
}
}
return cc;
}
private Stmt bindStatements(final Definition[] defs,
final int begin, final int end){
final Location loc = this.tag.getLocation();
return new Stmt(){
public Location getLocation(){
return loc;
}
public Class getType(){
return Component.class;
}
public Object run(final Dict frame, Runtime runtime){
return evalDefinitions(defs, begin, end, frame, runtime);
}
public String toString(){
return StringUtils.listArray("[", ", ", "]", defs);
}
};
}
private Stmt compileBinder(final String varname,
final Definition[] stmt,
final int begin, final int end){
final Location loc = this.tag.getLocation();
final Stmt body = bindStatements(stmt, begin, end);
return new Stmt(){
public Location getLocation(){
return loc;
}
public Class getType(){
return Binder.class;
}
public Object run(final Dict frame, Runtime runtime){
return evalBinder(varname, body, runtime, frame);
}
public String toString(){
return body.toString();
}
};
}
}//TagCompiler
private static final class LocalScope{
private final Dict ctxt;
private final Object[] keys;
private final Stmt[] stmts;
public String toString(){
return StringUtils.listArray("{",";","}",keys);
}
public Dict getScope() {
return ctxt;
}
public Stmt[] getDefinitions() {
return stmts;
}
public Object[] getKeys() {
return keys;
}
public LocalScope(Object[] keys, Stmt[] stmts, Dict ctxt) {
this.ctxt = ctxt;
this.stmts = stmts;
this.keys = keys;
}
}
static Stmt withScope(LocalScope scope, final Stmt stmt, final Location loc){
final Object[] keys = scope.getKeys();
final Stmt[] stmts = scope.getDefinitions();
return new Stmt(){
public String toString(){
return stmt.toString();
}
public Object run(Dict frame, Runtime runtime){
return stmt.run(newFrame(keys, stmts, frame, runtime), runtime);
}
public Class getType(){
return stmt.getType();
}
public Location getLocation(){
return loc;
}
};
}
private static Dict newFrame(Object[] keys, Stmt[] stmts,
Dict frame, final Runtime runtime){
final Thunk[] thunks = new Thunk[keys.length];
final Dict[] nframe = new Dict[1];
for(int i=0; i<keys.length; i++){
final Stmt stmt = stmts[i];
thunks[i] = new Thunk(keys[i], stmt.getLocation()){
Object evaluate(){
return stmt.run(nframe[0], runtime);
}
public Class getType(){
return stmt.getType();
}
};
}
nframe[0] = frame.puts(keys, thunks);
return nframe[0];
}
static Body evaluate(Object[] keys, Stmt[] stmts,
Runtime runtime,
Dict initial_frame){
final Dict frame = newFrame(keys,
stmts, initial_frame, runtime);
final Closure[] closures = new Closure[keys.length];
final Location[] locations = new Location[keys.length];
for(int i=0; i<keys.length; i++){
final Object key = keys[i];
//final Location iloc = stmts[i].getLocation();
closures[i] = (Closure)frame.get(key);
locations[i] = stmts[i].getLocation();
}
return new Body(keys, closures, locations);
}
private LocalScope compileLocalScope(final String tagname, Dict ctxt, List decs,
IdChecker global, SingletonMode singleton_mode, boolean is_global){
final Object[] keys = new Object[decs.size()];
final Bound[] bounds = new Bound[keys.length];
final HashMap locals = new HashMap(keys.length);
for(int i=0; i<keys.length; i++){
final Node node = (Node)decs.get(i);
final Location loc = node.getLocation();
if(node instanceof Tag){
final Tag tag = (Tag)node;
final String id = getIdAttribute(tag);
if(id==null){
if(!is_global){
throw new ConfigurationException(ID+" is required.",
loc);
}
else{
final Object aid = new AnonymousId(i);
keys[i] = aid;
bounds[i] = new Bound(aid, loc);
}
}
else{
if(locals.containsKey(id)){
throw new ConfigurationException("duplicate id: "+id, loc);
}
if(reserves.contains(id)){
throw new ConfigurationException("id "+id+" is reserved.", loc);
}
if(global!=null)
global.checkId(id, loc);
keys[i] = id;
bounds[i] = new Bound(id, loc);
locals.put(id, id);
}
}
else{
throw new ConfigurationException("only elements are allowed within "+tagname,
loc);
}
}
final Dict nctxt = ctxt.puts(keys, bounds);
final Stmt[] stmts = new Stmt[keys.length];
for(int i=0; i<keys.length; i++){
final Tag tag = (Tag)decs.get(i);
stmts[i] = compileTag(i, nctxt, tag, false, is_global, singleton_mode);
}
return new LocalScope(keys, stmts, nctxt);
}
private LocalScope compileLocalScope(Dict ctxt, Tag localtag){
if(localtag.getAttributes().size()>0){
throw new ConfigurationException("attributes not allowed on local tag",
localtag.getLocation());
}
final List decs = localtag.getSubNodes();
return compileLocalScope(localtag.getName(), ctxt, decs, null, null, false);
}
private static void checkDup(Map map, String key, String kind, Location loc){
if(key!=null && map.containsKey(key)){
throw new ConfigurationException("duplicate "+kind+": "+key, loc);
}
map.put(key, loc);
}
static final class Definition{
final Stmt stmt;
final String var;
final String id;
final String tagname;
Definition(String tagname, String id, String var, Stmt stmt) {
this.tagname = tagname;
this.stmt = stmt;
this.var = var;
this.id = id;
}
public String toString(){
return tagname;
}
}
private static boolean maybeType(Class expected, Class actual){
if(actual == null)
return true;
return ReflectionUtil.isCompatible(expected, actual);
}
private Stmt compileNode(
int ind, Dict ctxt, Node node, boolean var_permitted){
if(node instanceof CharacterData){
final CharacterData literal = (CharacterData)node;
return MyUtil.value(literal.getText(), node.getLocation());
}
else{
return compileLocalTag(ind, ctxt, (Tag)node, var_permitted);
}
}
private Stmt compileTag(int ind, Dict ctxt, Tag tag, boolean var_permitted, boolean is_global,
SingletonMode singleton_mode){
return new TagCompiler(ind, ctxt, tag, singleton_mode, var_permitted, is_global)
.compile();
}
private Stmt compileLocalTag(int ind, Dict local_ctxt, Tag tag, boolean var_permitted){
return compileTag(ind, local_ctxt, tag, var_permitted, false, null);
}
private void checkElementType(final Class expected,
final Stmt stmt){
final Class type = stmt.getType();
final Location loc = stmt.getLocation();
if(maybeType(expected, type)){
return;
}
else if(String.class.equals(type)){
if(!isDeserializable(expected)){
throw new ConfigurationException("cannot convert string to "
+ Misc.getTypeName(expected), loc);
}
return;
}
else{
throw new ConfigurationException("cannot convert " + Misc.getTypeName(type)
+" to " + Misc.getTypeName(expected), loc);
}
}
static ConfigurationException raise(String msg, Throwable e, Location loc){
if(e instanceof ConfigurationException){
throw (ConfigurationException)e;
}
else if(e instanceof InvocationTargetException){
throw raise(msg, ((InvocationTargetException)e).getTargetException(), loc);
}
throw new ConfigurationException(msg, e, loc);
}
static ConfigurationException raise(Throwable e, Location loc){
if(e instanceof ConfigurationException){
throw (ConfigurationException)e;
}
else if(e instanceof InvocationTargetException){
throw raise(((InvocationTargetException)e).getTargetException(), loc);
}
throw new ConfigurationException(e.getMessage(), e, loc);
}
private SideEffect anonymous_adder_invoker(
final NutDescriptor desc,
final Stmt stmt){
return new SideEffect(){
public String toString(){
return "add";
}
public void apply(Object obj, Dict frame, Runtime runtime){
try{
final Object v = stmt.run(frame, runtime);
final Method mtd = desc.getAnonymousAdder(v);
try{
mtd.invoke(obj, new Object[]{v});
}
catch(InvocationTargetException e){
throw new ConfigurationException(e.getTargetException(), stmt.getLocation());
}
}
catch(Throwable e){
throw raise(e, stmt.getLocation());
}
}
};
}
private SideEffect dyn_method_invoker(final Method mtd,
final Class param_type,
final Stmt stmt){
return new SideEffect(){
//final boolean to_component = Component.class.equals(param_type);
public String toString(){
return mtd.toString();
}
public void apply(Object obj, Dict frame, Runtime runtime){
try{
Object v = stmt.run(frame, runtime);
/*if(v instanceof Var){
v = ((Var)v).val;
if(to_component){
v = NutsUtils.asComponent(v);
}
}*/
try{
mtd.invoke(obj, new Object[]{
convert(param_type, v, stmt.getLocation())});
}
catch(RuntimeException e){
throw new ConfigurationException("failed to invoke " + mtd.getName()+
"("+Misc.getTypeName(param_type)+") with argument of type "
+(v==null?null:Misc.getTypeName(v.getClass()))
, e, stmt.getLocation());
}
}
catch(Throwable e){
throw raise(e, stmt.getLocation());
}
}
};
}
private static Object evalStmt(Stmt stmt,
Runtime runtime, Dict frame){
final Object r = stmt.run(frame, runtime);
return r;
}
private static Dict nextFrame(Definition c, Dict frame, Object cur){
final String id = c.id;
if(id!=null){
frame = frame.put(id, cur);
}
return frame;
}
/*
private static final class Var{
final Object val;
Var(Object val) {
this.val = val;
}
}*/
private static boolean isCompoundType(Class type){
return type.isArray() || List.class.isAssignableFrom(type)
|| Set.class.isAssignableFrom(type) || Collection.class.equals(type);
}
private static boolean isListType(Class type){
return Collection.class.equals(type) || List.class.isAssignableFrom(type);
}
private static boolean isRefType(Class type){
return Component.class.equals(type) || Binder.class.isAssignableFrom(type)
|| NutsFunction.class.isAssignableFrom(type);
}
private Stmt cast(final Class target_type, final Stmt stmt){
return new Stmt(){
public Class getType() {
return target_type;
}
public Object run(Dict frame, Runtime runtime) {
return convert(target_type, stmt.run(frame, runtime), getLocation());
}
public Location getLocation() {
return stmt.getLocation();
}
public String toString(){
return stmt.toString();
}
};
}
private static ComponentBinder evalBinder(final String varname,
final Stmt stmt, final Runtime runtime, final Dict frame){
return new ComponentBinder(){
public String toString(){
return "\\"+varname+"->"+stmt;
}
public Creator bind(Object v){
//final Var var = new Var(v);
Dict nframe = frame.put(varname, v);
final Object retval = evalStmt(stmt, runtime, nframe);
return NutsUtils.asComponent(retval);
}
public Class bindType(Class type) {
return stmt.getType();
}
public Verifiable verify(Class type) {
return Monad.verifyAs(stmt.getType());
}
};
}
AutoWiringMap getCustomWiringModes(){
return interpreter.getCustomWiringModes();
}
public File getBaseDir() {
return interpreter.getBaseDir();
}
public ResourceLoader getResourceLoader(){
return interpreter.getResourceLoader();
}
public ClassLoader getComponentClassLoader() {
return interpreter.getClassloader();
}
public ClassLoader getNutClassLoader(){
return interpreter.getClass().getClassLoader();
}
public Object findService(Object key) {
return interpreter.getServices().get(key);
}
public DefaultLifecycleManager getLifecycleManager(){
return interpreter.getLifecycleManager();
}
public boolean isDeserializable(Class type){
return interpreter.isDeserializable(type);
}
public Object deserialize(Class type, String text)
throws Throwable{
return interpreter.deserialize(type, text);
}
public void registerDeserializer(Class type, Deserializer deserializer,
boolean overriding, boolean mandatory){
interpreter.registerDeserializer(type, deserializer,
overriding, mandatory);
}
private int seq_no = 0;
public synchronized void registerEagerInstantiation(int ind, Object key, Component c){
interpreter.registerEagerInstantiation(new UID(module_id, ind, seq_no++, key), c);
}
public boolean isEagerlyInstantiating(){
return this.default_eager_mode;
}
public synchronized void registerDynamic(Object key, Object val,
boolean overridable, boolean overriding, Location loc){
interpreter.registerDynamic(key, val, overridable, overriding, loc);
}
private interface FromLiteral{
Object fromLiteral(String literal);
}
private static Stmt asStmt(Object v, Location loc){
if(v instanceof Stmt){
return (Stmt)v;
}
else return MyUtil.value(v, loc);
}
}