/*
** j###t ########## #### ####
** j###t ########## #### ####
** j###T "###L J###"
** ######P' ########## #########
** ######k, ########## T######T
** ####~###L ####
** #### q###L ########## .#####
** #### \###L ########## #####"
*/
package key.commands;
import key.*;
import java.io.*;
import java.util.StringTokenizer;
/**
* This is a wonderful command that can be instantiated and used
* for all sorts of 'constraints' when allowing users to set
* things.
*
* Basically, if one of the required properties is null, the
* user is prompted for it. Here are the properties (in the
* order the user needs to type them, for your usage displays)
*
* <UL>
* <LI> on - if not set, the user must put the name of the
* atom to be used here.
* <LI> set - the property to be set on that atom - must be
* put here, if not set in the command.
* </UL>
*
* Followed by the string value to set, if the maximum length
* (set on the command) isn't 0. If the length is zero, the
* property will simply be blanked. (set to "")
*/
public class ConstrainedSet extends Set
{
private static final long serialVersionUID = 3128665239895446113L;
public static final AtomicElement[] ELEMENTS =
{
AtomicElement.construct( ConstrainedSet.class, String.class, "blankFeedback",
AtomicElement.PUBLIC_FIELD,
"the message sent when the command is executed without arguments" ),
AtomicElement.construct( ConstrainedSet.class, String.class, "filledFeedback",
AtomicElement.PUBLIC_FIELD,
"the message sent after the command is executed with arguments" ),
AtomicElement.construct( ConstrainedSet.class, String.class, "setProperty",
AtomicElement.PUBLIC_FIELD,
"the constraint; the property to be set" ),
AtomicElement.construct( ConstrainedSet.class, String.class, "displayProperty",
AtomicElement.PUBLIC_FIELD,
"the property used to display the value, if it is different to the setProperty" ),
AtomicElement.construct( ConstrainedSet.class, Integer.TYPE, "maxLength",
AtomicElement.PUBLIC_FIELD,
"another constraint; the maximum length of the property" ),
AtomicElement.construct( ConstrainedSet.class, Atom.class, "in",
AtomicElement.PUBLIC_FIELD,
"another constraint; the container the atom is in" ),
AtomicElement.construct( ConstrainedSet.class, String.class, "on",
AtomicElement.PUBLIC_FIELD,
"another constraint; the atom to set the property on" ),
AtomicElement.construct( ConstrainedSet.class, String.class, "mustContain",
AtomicElement.PUBLIC_FIELD,
"the set value must contain this string" ),
AtomicElement.construct( ConstrainedSet.class, String.class, "subVerify",
AtomicElement.PUBLIC_FIELD,
"if true, prevents carats from appearing before the percent substitute code" )
};
public static final AtomicStructure STRUCTURE = new AtomicStructure( Set.STRUCTURE, ELEMENTS );
public static final char valueCode = 'v';
public static final char targetCode = 't';
public static final int DEFAULT_MAX_LENGTH = 60;
public String blankFeedback = "The current value for this room is '%v'";
public String filledFeedback = "The value now reads: %v";
public String setProperty = "portrait";
public String mustContain = "";
public String displayProperty = "fullPortrait";
public int maxLength = DEFAULT_MAX_LENGTH;
public boolean subVerify = false;
public Reference in = Key.shortcuts().getThis();
/**
* use a string here because we can't have a relative
* reference to a transient atom - which is precisely what
* we want to achieve.
*/
public String on = "here";
public ConstrainedSet()
{
setKey( "consset" ); // a default
usage = "<new message>";
}
public AtomicStructure getDeclaredStructure()
{
return( STRUCTURE );
}
public void run( Player p, StringTokenizer args, String fullLine, CategoryCommand caller, InteractiveConnection ic, Flags flags ) throws IOException
{
Container cin = null;
try
{
cin = (Container) in.get();
}
catch( OutOfDateReferenceException e )
{
in = Reference.EMPTY;
Log.error( "somebody set " + getId() + ".in wrong (disabled)", e );
disable();
}
catch( ClassCastException e )
{
in = Reference.EMPTY;
Log.error( "somebody set " + getId() + ".in wrong (disabled)", e );
disable();
}
Atom r;
if( cin == null )
cin = Key.instance();
String on = this.on;
// read the symbol from the arguments
if( on == null )
{
if( args.hasMoreTokens() )
on = args.nextToken();
else
{
// assuming they've set up the usage properly ;)
usage( ic );
return;
}
}
r = (Atom) new Search( on, cin ).result;
if( r == null )
{
ic.sendFailure( "Could not find '" + on + "' in " + cin.getId() );
return;
}
p.putCode( targetCode, r.getName() );
String pToSet = setProperty;
if( pToSet == null )
{
if( args.hasMoreTokens() )
{
pToSet = args.nextToken();
}
else
{
usage( ic );
return;
}
}
if( maxLength > 0 )
{
if( !args.hasMoreTokens() )
{
usage( ic );
{
Object o = r.getProperty( displayProperty );
if( o == null )
o = "";
p.putCode( valueCode, o.toString() );
}
ic.sendFeedback( Grammar.substitute( blankFeedback, p.getCodes() ) );
return;
}
String np = args.nextToken( "" );
if( maxLength < np.length() )
{
ic.sendError( "That is too long. Please use at most " + maxLength + " character" + ((maxLength!=1) ? "s." : "." ) );
return;
}
if( mustContain != null && mustContain.length() > 0 )
{
if( np.indexOf( mustContain ) == -1 )
{
ic.sendError( "The string must contain, at least '" + mustContain + "'." );
return;
}
}
if( subVerify )
{
if( np.indexOf( "^%" ) != -1 )
{
ic.sendError( "You may not place a carat (^^) before the substitute code (%)" );
return;
}
}
String fl = "." + pToSet + " " + np;
Atom o = p.getContext();
p.setContext( r );
super.run( p, new StringTokenizer( fl ), fl, caller, ic, flags );
p.setContext( o );
{
Object ob = r.getProperty( displayProperty );
if( ob == null )
ob = "";
p.putCode( valueCode, ob.toString() );
}
ic.send( Grammar.substitute( filledFeedback, p.getCodes() ) );
}
else
{
// as the length is 0 (or less, if they're stupid,
// we're just a blank command, not a set at all.
r.setProperty( pToSet, "" );
ic.send( Grammar.substitute( filledFeedback, p.getCodes() ) );
}
}
protected void sendOkay( InteractiveConnection ic )
{
}
}