/*
* (c) 2000-2009 Carlos G�mez Rodr�guez, todos los derechos reservados / all rights reserved.
* Licencia en license/bsd.txt / License in license/bsd.txt
*/
package eu.irreality.age;
import java.util.StringTokenizer;
import eu.irreality.age.scripting.ScriptException;
//package AetheriaAWT;
/**
* Descripci�n con comparando y m�scara, devuelve un texto u otro seg�n el estado.
*
*/
public class Description
{
//INSTANCE VARIABLES
/**Texto de la descripci�n.*/
protected String text;
/**Comparando.*/
protected long comparand; //pasar a long para comparar teniendo en cuenta estados relativos
/**M�scara.*/
protected long mask; //idem que la anterior
//Description class 2.0: Condition as predicate support
protected java.util.List conditions; //list of ObjectCode
//CLONE
public Object clone()
{
//Debug.println("CLONING: " + text);
Description nueva = new Description(text,comparand,mask);
java.util.List nuevasCondiciones = new java.util.ArrayList();
if ( conditions != null )
{
for ( int i = 0 ; i < conditions.size() ; i++ )
nuevasCondiciones.add ( ((ObjectCode)conditions.get(i)).cloneIfNecessary() );
}
//java.util.Collections.copy ( nuevasCondiciones , conditions ); //n�tese que el clon apunta al mismo object code (object code NO es modificable)
//nuevasCondiciones.addAll(conditions);
nueva.conditions = nuevasCondiciones;
return nueva;
}
//CONSTRUCTOR
public Description ( String ntext , long ncomparand , long nmask )
{
text = ntext;
comparand = ncomparand;
mask = nmask;
}
//METHODS
/**
* Nos dice si la descripci�n corresponde a un estado determinado, es decir, si se debe dar o no para dicho estado.
*
* @return Si la descripci�n corresponde al estado.
*/
public boolean matches ( long state )
{
//Debug.println("COMPARISON AHEAD");
//Debug.println("Outer comparand: " + state);
//Debug.println("Description comparand: " + comparand );
//Debug.println("Description mask: " + mask );
//Debug.println("Comparison result: " + (( mask & ( state ^ comparand ) ) == 0 ) );
return (( mask & ( state ^ comparand ) ) == 0 ); // si true, entonces es la descripci�n buscada (o una de ellas).
}
/**
* Nos dice si la descripci�n cumple las condiciones para ser mostrada,
* permitiendo utilizar variables en dichas condiciones, cuyas inicializaciones
* se pasan en variableInitialization.
* Por ejemplo, este array podr�a ser: { { viewer , mobile("fulano") , actor , mobile("mengano") } }.
*
* @return Si la descripci�n cumple las condiciones para ser mostrada.
*/
public boolean matchesConditions ( Entity context , Object[][] variableInitializations )
{
if ( conditions == null ) return true;
//{conditions not null}
for ( int i = 0 ; i < conditions.size() ; i++ )
{
ReturnValue rv = new ReturnValue(null);
try
{
//have to add additional code to set viewer
((ObjectCode)conditions.get(i)).run ( null , context , null , rv ,
variableInitializations
);
//Debug.println("Code ran.");
}
catch ( ScriptException te )
{
//error al evaluar condici�n.
te.printStackTrace();
continue;
}
if ( rv.getRetVal() instanceof Boolean )
{
//Debug.println("Boolean return value: " + ((Boolean)rv.getRetVal()).booleanValue() );
if ( ((Boolean)rv.getRetVal()).booleanValue() == false )
return false;
}
}
//{all conditions checked: true, not boolean or condition checking error}
return true;
}
/**
* Nos dice si la descripci�n cumple las condiciones para ser mostrada,
* incluyendo cualquiera relacionada con viewer (relationship-states)
* [parte del 2.0 description support]
* El viewer puede ser null.
*
* @return Si la descripci�n cumple las condiciones para ser mostrada.
*/
public boolean matchesConditions( Entity context , Entity viewer )
{
//Debug.println("Called matchesConditions( " + context + ", " + viewer + " )");
//viewer can be nullified if convenient
//removed 2011-05-14 (redundant):
//if ( viewer == null ) return matchesConditions ( context );
//old support
if ( viewer != null )
{
long comparand = (long)((Mobile)viewer).getRelationshipState( context )*((long)Math.pow(2,32)) + context.getState();
if ( !matches( comparand ) ) return false;
}
//Debug.println("Comparand matches.");
//new support
if ( conditions == null ) return true;
//Debug.println("Conditions not null. Size " + conditions.size());
//{conditions not null}
for ( int i = 0 ; i < conditions.size() ; i++ )
{
//Debug.println("Checking condition " + (i+1));
//Debug.println((ObjectCode)conditions.get(i));
ReturnValue rv = new ReturnValue(null);
try
{
//have to add additional code to set viewer
((ObjectCode)conditions.get(i)).run ( null , context , null , rv ,
new Object[][]
{
new Object[]
{
"viewer", viewer
}
}
);
//Debug.println("Code ran.");
}
catch ( ScriptException te )
{
//error al evaluar condici�n.
te.printStackTrace();
continue;
}
if ( rv.getRetVal() instanceof Boolean )
{
//Debug.println("Boolean return value: " + ((Boolean)rv.getRetVal()).booleanValue() );
if ( ((Boolean)rv.getRetVal()).booleanValue() == false )
return false;
}
}
//{all conditions checked: true, not boolean or condition checking error}
return true;
}
/**
* Nos dice si la descripci�n cumple las condiciones para ser mostrada.
* [parte del 2.0 description support]
*
* @return Si la descripci�n cumple las condiciones para ser mostrada.
*/
/*
public boolean matchesConditions( Entity context )
{
//old support
if ( !matches( context.getState() ) ) return false;
//new support
if ( conditions == null ) return true;
//{conditions not null}
for ( int i = 0 ; i < conditions.size() ; i++ )
{
ReturnValue rv = new ReturnValue(null);
try
{
((ObjectCode)conditions.get(i)).run ( null , context , null , rv ,
new Object[][]
{
new Object[]
{
"viewer", null
}
}
);
}
catch ( bsh.TargetError te )
{
//error al evaluar condici�n.
te.printStackTrace();
continue;
}
if ( rv.getRetVal() instanceof Boolean )
{
if ( ((Boolean)rv.getRetVal()).booleanValue() == false )
return false;
}
}
//{all conditions checked: true, not boolean or condition checking error}
return true;
}
*/
/**
* M�todo de acceso al texto de la descripci�n.
*
* @return Texto de la descripci�n.
*/
public String getText ( )
{
return text;
}
public org.w3c.dom.Node getXMLRepresentation ( org.w3c.dom.Document doc )
{
org.w3c.dom.Element suElemento = doc.createElement( "Description" );
String theText = text;
if ( theText.startsWith("P$") )
{
theText = theText.substring(2);
suElemento.setAttribute ( "properName" , "true" );
}
else if ( theText.startsWith("N$") )
{
theText = theText.substring(2);
suElemento.setAttribute ( "properName" , "false" );
}
org.w3c.dom.Text t = doc.createTextNode(theText);
suElemento.appendChild(t);
suElemento.setAttribute ( "stateComparand" , String.valueOf( comparand ) );
suElemento.setAttribute ( "stateMask" , String.valueOf ( mask ) );
if ( conditions != null )
{
for ( int i = 0 ; i < conditions.size() ; i++ )
{
ObjectCode oc = (ObjectCode)conditions.get(i);
org.w3c.dom.Element el = (org.w3c.dom.Element) oc.getXMLRepresentation(doc,"Condition");
suElemento.appendChild(el);
}
}
return suElemento;
}
public Description ( World w , org.w3c.dom.Node n ) throws XMLtoWorldException
{
if ( ! ( n instanceof org.w3c.dom.Element ) )
{
throw ( new XMLtoWorldException("Description node not element") );
}
else
{
org.w3c.dom.Element e = (org.w3c.dom.Element) n;
long comparand=0,mask=0; //si no se especifica en atributos stateComparand y stateMask, comparando y mascara valdran cero
try
{
if ( e.hasAttribute ( "stateComparand" ) )
comparand = Long.valueOf ( e.getAttribute( "stateComparand" ) ).longValue();
if ( e.hasAttribute ( "stateMask" ) )
mask = Long.valueOf ( e.getAttribute( "stateMask" ) ).longValue();
}
catch ( NumberFormatException nfe )
{
throw ( new XMLtoWorldException("Bad number format at attribute stateComparand or stateMask") );
}
//cogemos el texto
//block
{
boolean terminamos = false;
while ( !terminamos )
{
try
{
org.w3c.dom.Node hijo = n.getFirstChild();
while ( !(hijo instanceof org.w3c.dom.Text ) || hijo.getNodeValue().trim().length()==0 )
{
if ( hijo == null )
break;
hijo = hijo.getNextSibling();
}
org.w3c.dom.Text t = (org.w3c.dom.Text) (hijo);
if ( t == null )
throw ( new XMLtoWorldException("Description node containing no text node") );
//return ( new Description ( t.getData() , comparand , mask ) );
this.comparand=comparand;
this.mask=mask;
this.text=t.getData().trim(); //necessary because XML saving adds whitespace to text nodes
this.text = t.getData();
this.text = StringMethods.textualSubstitution( this.text, "\\n" , "\n" );
this.text = StringMethods.textualSubstitution( this.text, "\\t" , "\t" );
this.text = StringMethods.textualSubstitution( this.text, "\\s" , " " ); //escape character for space
terminamos = true;
}
catch ( ClassCastException cce )
{
continue;
}
}
}
//2.0 Description: predicate support begin
org.w3c.dom.NodeList nl = (org.w3c.dom.NodeList) e.getElementsByTagName("Condition");
for ( int i = 0 ; i < nl.getLength() ; i++ )
{
ObjectCode oc = new ObjectCode ( w, (org.w3c.dom.Element)nl.item(i) , true );
if ( conditions == null )
conditions = new java.util.ArrayList();
conditions.add(oc);
}
//2.0 Description: predicate support end
//proper name support: convert properName flag true to P$ and properName flag false to N$
//<Description properName=true>tal</Description>
if ( e.hasAttribute ( "properName" ) )
{
boolean isProper = Boolean.valueOf ( e.getAttribute( "properName" ) ).booleanValue();
if ( isProper ) text = "P$" + text;
else text = "N$" + text;
}
}
}
//metodos especificos para open y close descriptions
/**
* Returns whether this description is marked as a success or failure description
* (for open/close descriptions)
*/
public boolean hasSuccessMark()
{
return text.startsWith("SUCCESS:") || text.startsWith("EXITO:") || text.startsWith("FAIL:") || text.startsWith("SUCCESS:")
|| text.startsWith("FRACASO:");
}
/**
* Returns whether this description is marked as a success description (for
* open/close descriptions).
* @return
*/
public boolean isSuccessDescription()
{
return text.startsWith("SUCCESS:") || text.startsWith("EXITO:");
}
/**
* Returns whether this description is marked as a failure description (for
* open/close descriptions).
* @return
*/
public boolean isFailDescription()
{
return text.startsWith("FAIL:") || text.startsWith("FRACASO:");
}
/**
* Returns the pure text of the description without the success/fail mark.
* Transitional method. When the open/close/lock/unlock system is further
* modernised, the success/fail status should not be saved directly in the
* description text and therefore this should be equivalent to getText(),
* but at the moment that doesn't hold.
* @return
*/
public String getTextWithoutSuccessMark()
{
if ( hasSuccessMark() )
{
StringTokenizer st = new StringTokenizer(text,":");
st.nextToken();
return st.nextToken("").substring(1); //the 1 is to remove the : delimiter from the beginning.
}
else return text;
}
}