/*
** j###t ########## #### ####
** j###t ########## #### ####
** j###T "###L J###"
** ######P' ########## #########
** ######k, ########## T######T
** ####~###L ####
** #### q###L ########## .#####
** #### \###L ########## #####"
**
** $Id$
**
** Class History
**
** Date Name Description
** ---------|------------|-----------------------------------------------
** 21Feb98 subtle created
**
*/
package key;
import java.util.StringTokenizer;
/**
* This class is used to contain search parameters for the routines
* which iterate through a string to produce a symbol. For instance,
* while matching '/players/subtle.name', a Search object will be
* created for the duration of the search, to hold transient information
* required for abstraction purposes.
*
* @see key.Searchable
*/
public final class Search
{
public StringTokenizer st;
/**
* Holds the last atom we passed through. This is
* necessary in case we wish to return an AtomicElement
* from the search, in which case the atom that the
* element is on is very important if we wish to get
* or set the actual value.
*/
public Atom lastAtom;
/**
* holds the final result of our search, properties & transient
* stepped through
*/
public Object result;
/**
* holds the first matched result of our search, useful for set() routines
* that need to access the low-level provider. specifically, this is
* the object before it has been notified that it is the end result
* of a search
*/
public Object matchedResult;
public boolean dirty;
public boolean resolveReference = true;
public boolean resolve = true;
public Search( String s, Atom relativeTo )
{
doSearch( s, relativeTo );
}
public Search( String s, Atom relativeTo, boolean resolveReferences, boolean resolve )
{
this.resolveReference = resolveReferences;
this.resolve = resolve;
doSearch( s, relativeTo );
}
public void doSearch( String s, Atom relativeTo ) throws InvalidSearchException
{
int count = 0;
int len = s.length();
if( len <= 0 )
{
result = relativeTo;
st = new StringTokenizer( s, "./@", true );
}
else
{
char c = s.charAt( 0 );
Object oldResult;
switch( c )
{
case '/':
if( len > 1 && s.charAt( 1 ) == '.' )
s = s.substring( 1 );
else
;
// don't strip the leading slash, it's
// needed to show that we want an element,
// not a property
relativeTo = Key.instance();
break;
case '~':
s = s.substring( 1 );
relativeTo = Key.shortcuts();
break;
case '#':
s = s.substring( 1 );
String number = null;
// try to strip the number off the front
for( int i = 0; i < s.length(); i++ )
{
if( !Character.isDigit( s.charAt( i ) ) )
{
number = s.substring( 0, i );
s = s.substring( i );
break;
}
}
if( number == null )
{
number = s;
s = "";
}
int index = 0;
try
{
index = Integer.parseInt( number );
}
catch( NumberFormatException e )
{
throw new InvalidSearchException( e.toString() + " while matching id #" );
}
relativeTo = Registry.instance.get( index );
break;
}
st = new StringTokenizer( s, "./@", true );
lastAtom = relativeTo;
result = relativeTo;
while( result instanceof Searchable && st.hasMoreTokens() && count < 20 )
{
((Searchable)result).search( this );
count++;
}
if( count >= 20 )
throw new InvalidSearchException( "search path too deep (> 20)" );
}
Object lastResult = null;
count = 0;
matchedResult = result;
//
// one last time notifies result of our intention
// to use it as the search results. this permits transient
// matches to resolve, or offline atoms with stubs to be
// loaded and returned.
//
// if one of these atoms returns a different result, we need
// to notify that different result, as well, hence the while
//
// again, we use 'count' to bound the limit of these to 20, just
// to stop circular searches
//
if( resolve )
{
if( !resolveReference )
{
while
(
(result instanceof Searchable) &&
!(
(result instanceof Reference) &&
!(((Reference)result).isLoaded())
) &&
(lastResult != result) &&
(count < 20)
)
{
lastResult = result;
((Searchable)result).search( this );
count++;
}
}
else
{
while( (result instanceof Searchable) && (lastResult != result) && (count < 20) )
{
lastResult = result;
((Searchable)result).search( this );
count++;
}
}
if( count >= 20 )
throw new InvalidSearchException( "search resolve path too deep (> 20)" );
}
}
}