/* * ArrayCmd.java * * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart * * RCS @(#) $Id: ArrayCmd.java,v 1.4 2003/01/10 01:57:57 mdejong Exp $ * */ using System; using System.Collections; namespace tcl.lang { /// This class implements the built-in "array" command in Tcl. class ArrayCmd : Command { internal static Type procClass = null; private static readonly string[] validCmds = new string[] { "anymore", "donesearch", "exists", "get", "names", "nextelement", "set", "size", "startsearch", "unset" }; internal const int OPT_ANYMORE = 0; internal const int OPT_DONESEARCH = 1; internal const int OPT_EXISTS = 2; internal const int OPT_GET = 3; internal const int OPT_NAMES = 4; internal const int OPT_NEXTELEMENT = 5; internal const int OPT_SET = 6; internal const int OPT_SIZE = 7; internal const int OPT_STARTSEARCH = 8; internal const int OPT_UNSET = 9; /// This procedure is invoked to process the "array" Tcl command. /// See the user documentation for details on what it does. /// public TCL.CompletionCode cmdProc( Interp interp, TclObject[] objv ) { Var var = null, array = null; bool notArray = false; string varName, msg; int index;//, result; if ( objv.Length < 3 ) { throw new TclNumArgsException( interp, 1, objv, "option arrayName ?arg ...?" ); } index = TclIndex.get( interp, objv[1], validCmds, "option", 0 ); // Locate the array variable (and it better be an array). varName = objv[2].ToString(); Var[] retArray = Var.lookupVar( interp, varName, null, 0, null, false, false ); // Assign the values returned in the array if ( retArray != null ) { var = retArray[0]; array = retArray[1]; } if ( ( var == null ) || !var.isVarArray() || var.isVarUndefined() ) { notArray = true; } // Special array trace used to keep the env array in sync for // array names, array get, etc. if ( var != null && var.traces != null ) { msg = Var.callTraces( interp, array, var, varName, null, ( TCL.VarFlag.LEAVE_ERR_MSG | TCL.VarFlag.NAMESPACE_ONLY | TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.TRACE_ARRAY ) ); if ( (System.Object)msg != null ) { throw new TclVarException( interp, varName, null, "trace array", msg ); } } switch ( index ) { case OPT_ANYMORE: { if ( objv.Length != 4 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName searchId" ); } if ( notArray ) { errorNotArray( interp, objv[2].ToString() ); } if ( var.sidVec == null ) { errorIllegalSearchId( interp, objv[2].ToString(), objv[3].ToString() ); } SearchId e = var.getSearch( objv[3].ToString() ); if ( e == null ) { errorIllegalSearchId( interp, objv[2].ToString(), objv[3].ToString() ); } if ( e.HasMore ) { interp.setResult( "1" ); } else { interp.setResult( "0" ); } break; } case OPT_DONESEARCH: { if ( objv.Length != 4 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName searchId" ); } if ( notArray ) { errorNotArray( interp, objv[2].ToString() ); } bool rmOK = true; if ( var.sidVec != null ) { rmOK = ( var.removeSearch( objv[3].ToString() ) ); } if ( ( var.sidVec == null ) || !rmOK ) { errorIllegalSearchId( interp, objv[2].ToString(), objv[3].ToString() ); } break; } case OPT_EXISTS: { if ( objv.Length != 3 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName" ); } interp.setResult( !notArray ); break; } case OPT_GET: { // Due to the differences in the hashtable implementation // from the Tcl core and Java, the output will be rearranged. // This is not a negative side effect, however, test results // will differ. if ( ( objv.Length != 3 ) && ( objv.Length != 4 ) ) { throw new TclNumArgsException( interp, 2, objv, "arrayName ?pattern?" ); } if ( notArray ) { return TCL.CompletionCode.RETURN; } string pattern = null; if ( objv.Length == 4 ) { pattern = objv[3].ToString(); } Hashtable table = (Hashtable)var.value; TclObject tobj = TclList.newInstance(); string arrayName = objv[2].ToString(); string key, strValue; Var var2; // Go through each key in the hash table. If there is a // pattern, test for a match. Each valid key and its value // is written into sbuf, which is returned. // FIXME : do we need to port over the 8.1 code for this loop? for ( IDictionaryEnumerator e = table.GetEnumerator(); e.MoveNext(); ) { key = ( (string)e.Key ); var2 = (Var)e.Value; if ( var2.isVarUndefined() ) { continue; } if ( (System.Object)pattern != null && !Util.stringMatch( key, pattern ) ) { continue; } strValue = interp.getVar( arrayName, key, 0 ).ToString(); TclList.append( interp, tobj, TclString.newInstance( key ) ); TclList.append( interp, tobj, TclString.newInstance( strValue ) ); } interp.setResult( tobj ); break; } case OPT_NAMES: { if ( ( objv.Length != 3 ) && ( objv.Length != 4 ) ) { throw new TclNumArgsException( interp, 2, objv, "arrayName ?pattern?" ); } if ( notArray ) { return TCL.CompletionCode.RETURN; } string pattern = null; if ( objv.Length == 4 ) { pattern = objv[3].ToString(); } Hashtable table = (Hashtable)var.value; TclObject tobj = TclList.newInstance(); string key; // Go through each key in the hash table. If there is a // pattern, test for a match. Each valid key and its value // is written into sbuf, which is returned. for ( IDictionaryEnumerator e = table.GetEnumerator(); e.MoveNext(); ) { key = (string)e.Key; Var elem = (Var)e.Value; if ( !elem.isVarUndefined() ) { if ( (System.Object)pattern != null ) { if ( !Util.stringMatch( key, pattern ) ) { continue; } } TclList.append( interp, tobj, TclString.newInstance( key ) ); } } interp.setResult( tobj ); break; } case OPT_NEXTELEMENT: { if ( objv.Length != 4 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName searchId" ); } if ( notArray ) { errorNotArray( interp, objv[2].ToString() ); } if ( var.sidVec == null ) { errorIllegalSearchId( interp, objv[2].ToString(), objv[3].ToString() ); } SearchId e = var.getSearch( objv[3].ToString() ); if ( e == null ) { errorIllegalSearchId( interp, objv[2].ToString(), objv[3].ToString() ); } if ( e.HasMore ) { Hashtable table = (Hashtable)var.value; DictionaryEntry entry = e.nextEntry(); string key = (string)entry.Key; Var elem = (Var)entry.Value; if ( ( elem.flags & VarFlags.UNDEFINED ) == 0 ) { interp.setResult( key ); } else { interp.setResult( "" ); } } break; } case OPT_SET: { if ( objv.Length != 4 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName list" ); } int size = TclList.getLength( interp, objv[3] ); if ( size % 2 != 0 ) { throw new TclException( interp, "list must have an even number of elements" ); } int i; string name1 = objv[2].ToString(); string name2, strValue; // Set each of the array variable names in the interp for ( i = 0; i < size; i++ ) { name2 = TclList.index( interp, objv[3], i++ ).ToString(); strValue = TclList.index( interp, objv[3], i ).ToString(); interp.setVar( name1, name2, TclString.newInstance( strValue ), 0 ); } break; } case OPT_SIZE: { if ( objv.Length != 3 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName" ); } if ( notArray ) { interp.setResult( 0 ); } else { Hashtable table = (Hashtable)var.value; int size = 0; for ( IDictionaryEnumerator e = table.GetEnumerator(); e.MoveNext(); ) { Var elem = (Var)e.Value; if ( ( elem.flags & VarFlags.UNDEFINED ) == 0 ) { size++; } } interp.setResult( size ); } break; } case OPT_STARTSEARCH: { if ( objv.Length != 3 ) { throw new TclNumArgsException( interp, 2, objv, "arrayName" ); } if ( notArray ) { errorNotArray( interp, objv[2].ToString() ); } if ( var.sidVec == null ) { var.sidVec = new ArrayList( 10 ); } // Create a SearchId Object: // To create a new SearchId object, a unique string // identifier needs to be composed and we need to // create an Enumeration of the array keys. The // unique string identifier is created from three // strings: // // "s-" is the default prefix // "i" is a unique number that is 1+ the greatest // SearchId index currently on the ArrayVar. // "name" is the name of the array // // Once the SearchId string is created we construct a // new SearchId object using the string and the // Enumeration. From now on the string is used to // uniquely identify the SearchId object. int i = var.NextIndex; string s = "s-" + i + "-" + objv[2].ToString(); IDictionaryEnumerator e = ( (Hashtable)var.value ).GetEnumerator(); var.sidVec.Add( new SearchId( e, s, i ) ); interp.setResult( s ); break; } case OPT_UNSET: { string pattern; string name; if ( ( objv.Length != 3 ) && ( objv.Length != 4 ) ) { throw new TclNumArgsException( interp, 2, objv, "arrayName ?pattern?" ); } if ( notArray ) { //Ignot this error -- errorNotArray(interp, objv[2].ToString()); break; } if ( objv.Length == 3 ) { // When no pattern is given, just unset the whole array interp.unsetVar( objv[2], 0 ); } else { pattern = objv[3].ToString(); Hashtable table = (Hashtable)( ( (Hashtable)var.value ).Clone() ); for ( IDictionaryEnumerator e = table.GetEnumerator(); e.MoveNext(); ) { name = (string)e.Key; Var elem = (Var)e.Value; if ( var.isVarUndefined() ) { continue; } if ( Util.stringMatch( name, pattern ) ) { interp.unsetVar( varName, name, 0 ); } } } break; } } return TCL.CompletionCode.RETURN; } /// Error meassage thrown when an invalid identifier is used /// to access an array. /// /// /// currrent interpreter. /// /// var is the string representation of the /// variable that was passed in. /// private static void errorNotArray( Interp interp, string var ) { throw new TclException( interp, "\"" + var + "\" isn't an array" ); } /// Error message thrown when an invalid SearchId is used. The /// string used to reference the SearchId is parced to determine /// the reason for the failure. /// /// /// currrent interpreter. /// /// sid is the string represenation of the /// SearchId that was passed in. /// internal static void errorIllegalSearchId( Interp interp, string varName, string sid ) { int val = validSearchId( sid.ToCharArray(), varName ); if ( val == 1 ) { throw new TclException( interp, "couldn't find search \"" + sid + "\"" ); } else if ( val == 0 ) { throw new TclException( interp, "illegal search identifier \"" + sid + "\"" ); } else { throw new TclException( interp, "search identifier \"" + sid + "\" isn't for variable \"" + varName + "\"" ); } } /// A valid SearchId is represented by the format s-#-arrayName. If /// the SearchId string does not match this format than it is illegal, /// else we cannot find it. This method is used by the /// ErrorIllegalSearchId method to determine the type of error message. /// /// /// pattern[] is the string use dto identify the SearchId /// /// 1 if its a valid searchID; 0 if it is not a valid searchId, /// but it is for the array, -1 if it is not a valid searchId and NOT /// for the array. /// private static int validSearchId( char[] pattern, string varName ) { int i; if ( ( pattern[0] != 's' ) || ( pattern[1] != '-' ) || ( pattern[2] < '0' ) || ( pattern[2] > '9' ) ) { return 0; } for ( i = 3; ( i < pattern.Length && pattern[i] != '-' ); i++ ) { if ( pattern[i] < '0' || pattern[i] > '9' ) { return 0; } } if ( ++i >= pattern.Length ) { return 0; } if ( varName.Equals( new string( pattern, i, ( pattern.Length - i ) ) ) ) { return 1; } else { return -1; } } } }