package jfun.yan.xml.nut;
import java.net.MalformedURLException;
import jfun.yan.Component;
import jfun.yan.ParameterBinder;
import jfun.yan.PropertyBinder;
import jfun.yan.xml.ConfigurationException;
import jfun.yan.xml.Location;
import jfun.yan.xml.NutEnvironment;
import jfun.yan.xml.NutsUtils;
/**
* The super class of all Nut classes.
* <p>
* Nut stands for Name based User defineable Tag.
* A method whose name and signature is of a certain pre-defined pattern is
* called when a sub-element with corresponding name is interpreted
* inside the xml tag.
* </p>
* <p>
* For example, for the following tag:<br>
* <pre>
* <ctor class="A">
* <arg ind="0" val="1">
* </ctor>
* </pre>
* </p>
* <p>
* Method <code>setClass</code> is called for the "class" attribute,
* which is of course same as the standard Java Bean convention.
* In fact, a Nut class is always a Java Bean because it has to
* have a default public constructor and some setters to
* set attributes.
* </p>
* <p>
* Method <code>addArg</code> is called for each sub-element.
* And the parameter type of addArg should be another Nut class
* to support further "tagging".
* </p>
* <p>
* For tags simply with a list of sub-elements whose names are
* irrelevant, the <code>set</code> method with an array parameter is called.
* <br>
* For example:
* <pre>
* <array>
* <return val="1">
* <ctor class="B">
* <method class="C" name="f">
* </array>
* </pre>
* </p>
* <p>
* Finally, a Nut class can optionally have a method named <code>"eval"</code>.
* If this method exist, it is invoked when the tag is evaluated.
* If the return type is void or the "eval" method does not exist,
* the Nut object itself is used as the evaluation result.
* Otherwise, the return value of the "eval" method is used.
* </p>
* @author Ben Yu
* Nov 9, 2005 10:08:11 PM
*/
public abstract class Nut implements java.io.Serializable{
private Location loc;
private NutEnvironment env;
private String tagname;
private String id;
private boolean globallyDefined;
private int seq_no;
/**
* Is this tag defined globally?
*/
public boolean isGloballyDefined(){
return globallyDefined;
}
/**
* Dynamically register an object.
* @param key the key of the object.
* @param val the object.
* @param overridable whether this registration is overridable.
* @param overriding do we override when the same key is already used
* by another dynamically registered entry.
*/
public void register(Object key, Object val,
boolean overridable, boolean overriding){
env.registerDynamic(key, val, overridable, overriding, loc);
}
/**
* Get the sequence number starting from 0.
* This number is relative to the order the tag is defined within the current scope.
*/
public int getSequenceNumber(){
return seq_no;
}
/**
* Get the id of this tag.
* @return the id.
*/
public String getId() {
return id;
}
/**
* Set the id of the tag.
* @param id the id.
*/
public void setId(String id) {
this.id = id;
}
public String toString() {
return tagname;
}
/**
* Get this tag's location within the configuration file.
*/
public Location getTagLocation(){
return loc;
}
/**
* To set the sequence number of the tag in the enclosing scope.
* @param seq the sequence number.
*/
public void initSequenceNumber(int seq){
this.seq_no = seq;
}
/**
* To set whether the tag is defined globally.
* @param gd the flag.
*/
public void initGloballyDefined(boolean gd){
this.globallyDefined = gd;
}
/**
* The framework calls this method to set the location.
* @param loc the location.
*/
public void initTagLocation(Location loc){
this.loc = loc;
}
/**
* The framework calls this method to set the environment.
* @param env the environment.
*/
public void initNutEnvironment(NutEnvironment env){
this.env = env;
}
/**
* Get the class loader used to load the component classes.
*/
public ClassLoader getComponentClassLoader() {
return env.getComponentClassLoader();
}
/**
* Get a ClassLoader object that uses the current ClassLoader
* as parent, and tries an alternative classpath if the resource
* or class is not found.
*
* @param classpath the alternative classpath. null if unspecified.
* @return the ClassLoader object.
*/
public ClassLoader getNutClassLoader(String classpath){
try{
return NutsUtils.getClassLoader(getClass().getClassLoader(),
classpath,
env.getBaseDir());
}
catch(MalformedURLException e){
throw new ConfigurationException("invalid classpath",
loc);
}
}
/**
* The framework calls this method to set the tag name.
* @param name the tag name.
*/
public void initTagName(String name){
this.tagname = name;
}
/**
* Get the environment that the Nut object is running in.
*/
public NutEnvironment getNutEnvironment() {
return env;
}
/**
* Convert an object to a target type.
* @param target_type the target type.
* @param v the object to be converted.
* @return the object of the target type.
*/
public Object convert(Class target_type, Object v){
return env.convert(target_type, v, loc);
}
/**
* Transforms a Component so that the instantiated instance
* is converted to the target type.
* @param target_type the target type.
* @param c the component.
* @return the new Component with the conversion in effect.
*/
public Component cast(final Class target_type, Component c){
return env.cast(target_type, c, this.getTagLocation());
}
/**
* Get the tag name.
*/
public String getTagName() {
return tagname;
}
/**
* Register a Component to be eagerly instantiated using the current tag's id.
* @param c the Component to be eagerly instantiated.
*/
public void registerEagerInstantiation(Component c){
if(!globallyDefined){
raise("only global tags can be registered as eager instantiated");
}
/*
if(id==null){
raise("id is required to eager initialize a Component");
}*/
env.registerEagerInstantiation(seq_no, id, c);
}
/**
* Throws a ConfigurationException with the current tag name and location information.
* @param msg the error message.
* @return This method never returns. The return type is for
* callers to get around of the type system so that you can say
* "throw raise(...)".
*/
public ConfigurationException raise(String msg){
throw new ConfigurationException(tagname+": " + msg, loc);
}
/**
* Throws a ConfigurationException with the current tag name and location information.
* @param e the nested exception.
* @return This method never returns. The return type is for
* callers to get around of the type system so that you can say
* "throw raise(...)".
*/
public ConfigurationException raise(Throwable e){
throw new ConfigurationException(tagname+": " + e.getMessage(), e, loc);
}
/**
* Make sure an object is not null.
* Proper error message is reported otherwise.
* @param attrname the name of the checked attribute.
* @param v the attribute value.
*/
protected void checkMandatory(String attrname, Object v){
if(v==null)
raise("missing mandatory attribute <"+attrname +">");
}
/**
* Make sure at least one of two attributes is present.
* @param name1 the name of the first attribute.
* @param v1 the value of the first attribute.
* @param name2 the name of the second attribute.
* @param v2 the value of the second attribute.
*/
protected void checkMandatory(String name1, Object v1,
String name2, Object v2){
if(v1==null&&v2==null)
raise("either <"+name1+"> or <"+ name2+ "> has to be specified.");
}
/**
* Makes sure that a certain attribute is not set yet
* to avoid duplicate setting.
* @param attrname the attribute name.
* @param v the attribute value.
*/
protected void checkDuplicate(String attrname, Object v){
if(v!=null){
raise("attribute <"+attrname+"> already specified.");
}
}
/**
* Makes sure the array only contains one element.
* This is useful for tags accepting only one sub-element.
* @param vals the value array.
*/
/*protected void checkSingleChild(Object[] vals){
if(vals.length>1)
throw raise("only one sub-element is allowed");
}*/
/**
* To get the ParameterBinder object that encapsulates the auto-wiring strategy
* for a autowire mode specified by the mode name.
*
* @param mode the mode name.
* @return the ParameterBinder object for the auto-wiring strategy.
* null is returned if this indicates a manual-wire.
*/
public ParameterBinder getParameterWiring(String mode){
return env.getParameterWiringMode(mode, loc);
}
/**
* To get the PropertyBinder object that encapsulates the auto-wiring strategy
* for a autowire mode specified by the mode name.
*
* @param mode the mode name.
* @return the PropertyBinder object for the auto-wiring strategy.
* null is returned if this indicates a manual-wire.
*/
public PropertyBinder getPropertyWiring(String mode){
return env.getPropertyWiringMode(mode, loc);
}
//should have a method eval(). return type can be anything.
}