################################################################################ # TSL-LIBRARY: EMOS_DDT_ACCESS_lib ################################################################################ # Copyright (C) 2000 EMOS Computer Consulting GmbH # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # For further information please contact: # # Dean Rajovic # EMOS Computer Consulting GmbH # Oskar-Messter-Straße 25 # 85737 Ismaning # Germany # tel.: +49 89 608 765-0 # mailto:drajovic@emos.de # http://www.emos.de ################################################################################ # $Revision: 1.2 $ # $Author: drajovic $ # $Date: 2005/01/28 11:18:42 $ # $Archive: /MERCURY/Projects/EMOS_GPL/DDT/emos_ddt_access_lib/script $ # $NoKeywords: $ ################################################################################ #**# #* This library defines a sequential forward "iterator" (as much as one can #* emulate a real iterator in a language such as TSL) for data tables #* (ddt-interface). #* The main benefits of this iterator are: #* 1.) you can loop through any table in an uniform way, #* 2.) you can elegantly define a set of entries to be iterated through, #* 3.) you can simultaneously have iterators for multiple data tables #* (ane at a time per table!) #* 4.) you can use it to iterate anything, not only data tables. #*<p>NOTE! #* To understand how this iterator works use the analogy of selecting pages to #* be printed in a word-processing program. This sort of dialogs typically #* allow you to specify the RANGE of pages by connecting a first and the last #* page in the range with a hyphen (e.g. 3-9 ) and INDIVIDUAL pages by #* separating them with commas (e.g. 1,5,9,15-17,20). Exactly the same syntax #* is used with this iterator. So, if you call #*<pre> DDT_ACCESS_init( table, "1-3,7,9-11" );</pre> #* the #*<pre> DDT_ACCESS_get_next( table );</pre> #* will return you the sequence 1,2,3,7,9,10,11. #* #* This iterator also accepts non-numeric expressions such as #*<pre> DDT_ACCESS_init( table, "foo,bar,anyway" );</pre> #* Note however that this iterator does not read a data table in any way. So #* if your table contains following five columns #*<pre> aaa;foo;ccc;bar;eee</pre> #* the expression #*<pre> DDT_ACCESS_init( table, "foo-bar" );</pre> #* will return you "foo-bar" instead of "foo", "ccc", "bar" as you might have #* expected. If you use alpha names you, should know them in advance, i.e. #*<pre> DDT_ACCESS_init( table, "foo,ccc,bar" );</pre> #* #* You might think of this as a serious limitation, but once you learn how #* EMOS uses data tables in its framework, you will probably apreciate the #*/ #____________________________________________________ # local variables #____________________________________________________ static testNames[]; static testCount[]; static testIdx[]; #** #* Initialises the standard access algorithm. The <code>tests</code> can be specified #* with syntax described on library-level (e.g. "1-5,7,9,aaa,xxx,12-35). #* @param table (in) unique name for the iterator (normaly, name of the data table) #* @param tests (in) test names (e.g. "1-15" or "1-3,5,7" or "a,b,c", etc.) #* @return #* E_OK: initialisation succeeded #* !E_OK: invalid test set #*/ public function DDT_ACCESS_init( in table, in tests ) { auto count = init_count( table, tests ); if ( count == 0 ) return E_ILLEGAL_PARAMETER; testCount[table] = count; testIdx[table] = 0; return E_OK; } #** #* Removes access for the given <code<table</code> name and frees the internal buffers. #* @param table (in) the name od the iterator (data table) #*/ public function DDT_ACCESS_clean ( in table ) { delete_dimension( testNames, table ); delete_dimension( testCount, table ); delete_dimension( testIdx, table ); } #** #* Indicates whether a subsequent call to DDT_ACCESS_get_next() is about to #* succeed or not. #* @param table (in) the name od the iterator (data table) #* @return #* TRUE: there are still some tests left to be processed #* FALSE: all tests processed #*/ public function DDT_ACCESS_has_more( in table ) { return testIdx[table] < testCount[table]; } #** #* Returns the name of the next test to be processed. More specifically, it #* returns the next available name from the set of names that were specified #* by the DDT_ACCES_init() command. #* @param table (in) the name od the iterator (data table) #* @return #* !"": a test name #* "": error ocurred #*/ public function DDT_ACCESS_get_next( in table ) { if ( DDT_ACCESS_has_more( table ) ) return testNames[table, testIdx[table]++]; else return ""; } #** #* Analyses the given string <code>tests</code> and builds an array that contains all #* individual values that were specified. #* @param table (in) table dimension #* @param tests (in) test names in format (1,3-9) #* @return # number of tests to be processed #*/ static function init_count( in table, in tests ) { auto i, j; auto x, y, z, b; auto t1, tests1[]; auto t2, tests2[]; delete_dimension( testNames, table ); t1 = split( tests, tests1, "," ); j = 0; for( i=1; i <= t1; i++ ) { t2 = split( tests1[i], tests2, "-" ); switch ( t2 ) { case 0: break; case 1: testNames[table, j++] = tests2[1]; break; default: if ( get_range( tests2[1], tests2[2], x, y, b ) != E_OK ) { testNames[table, j++] = tests1[i]; break; } if ( x < y ) while ( x <= y ) testNames[table, j++] = b & x++; else while ( x >= y ) testNames[table, j++] = b & x--; break; } for( z=1; z<=t2; z++ ) delete tests2[z]; } return j; } static function get_range( in from, in to, out x, out y, out b ) { extern RSTART, RLENGTH; y = to*1; if ( !y ) return E_OUT_OF_RANGE; if ( match( from, "[0-9][0-9]*" ) ) { if ( RSTART == 1 ) { x = substr( from, RSTART, RLENGTH )*1; b = substr( from, RSTART+RLENGTH ); return E_OK; } if ( RSTART+RLENGTH-1 == length( from ) ) { b = substr( from, 1, RSTART-1 ); x = substr( from, RSTART )*1; return E_OK; } } return E_OUT_OF_RANGE; } #** #* This function deletes a dimension from a multidimensional array. #*<p>WARNING!<p> #* It will delete ALL dimensions with the same name. For example #* assume your three-dimensional array "arr" has the following content: #*<pre> arr["x",1,"x"] = "x1x"; #* arr["x",1,"y"] = "x1y"; #* arr["y",1,"x"] = "y1x"; #* arr["y",1,"y"] = "y1Y"; #*</pre> #* then the following command #*<pre> delete_dimension( arr, "x" );</pre> #* will delete everything BUT #*<pre> arr["y",1,"y"] = "y1Y";</pre> #* @param arr (inout) array to be processed #* @param dim (in) dimension(s) to be removed #*/ static function delete_dimension( inout arr[], in dim ) { auto i; for ( i in arr ) if ( match (i, dim) ) delete arr[i]; } #** #* Test with valid values #*/ static function col_test_valid() { auto i, valid[], tab = "tab"; valid[0] = "1"; valid[1] = "1,2"; valid[2] = "1-3"; valid[3] = "1,3-5,7"; valid[4] = "30-33,40-42"; valid[5] = "abc"; valid[6] = "aaa,bbb,ccc"; valid[7] = "11-11"; valid[8] = "11-5"; for ( i = 0; i < 9; i++ ) { print( "test: " & valid[i] ); DDT_ACCESS_init( tab, valid[i] ); while( DDT_ACCESS_has_more( tab ) ) print( " " & DDT_ACCESS_get_next( tab ) ); DDT_ACCESS_clean( tab ); } } #** #* Test with invalid values #*/ static function col_test_invalid() { auto i, invalid[], tab = "tab"; invalid[0] = ""; invalid[1] = ",-,"; invalid[2] = "-3"; invalid[3] = "1-, 3-5 ,-"; invalid[4] = "1-3-5,42"; invalid[5] = "abc-xxx"; invalid[6] = "3-1"; for ( i = 0; i < 7; i++ ) { print( "test: " & invalid[i] ); DDT_ACCESS_init( tab, invalid[i] ); while( DDT_ACCESS_has_more( tab ) ) print( " " & DDT_ACCESS_get_next( tab ) ); DDT_ACCESS_clean( tab ); } } # Uncomment the following lines to run the test #static const x = col_test_valid(); #static const y = col_test_invalid(); ########################################################################### # TSL-LIBRARY: EMOS_DDT_ACCESS_lib ###########################################################################