This patch adds the following functionality to the <foreach> element:
- multiple delimiters are allowed
- multiple properties are allowing the item="Line" case, but only if delimiter(s) are specified
- trim="" property allows you to specify trimming whitespace from begin, end or both parts of a string


You can now do this (using Scott's example):

<foreach item="Line" in="properties.csv" trim="both" delim="," property="x,y">
   <property name="${x}" value="${y}"/>
</foreach>

where properties.cvs is
prop1, value
prop2, value


New tests added for new functionality, all tests pass w/current CVS.


Note that my earlier patch from today is included in this one - sorry!

? patch.txt
Index: src/NAnt.Core/Tasks/CopyTask.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.Core/Tasks/CopyTask.cs,v
retrieving revision 1.4
diff -u -r1.4 CopyTask.cs
--- src/NAnt.Core/Tasks/CopyTask.cs     26 Feb 2003 07:02:30 -0000      1.4
+++ src/NAnt.Core/Tasks/CopyTask.cs     3 Mar 2003 17:34:34 -0000
@@ -126,6 +126,8 @@
                             Log.WriteLineIf(Verbose, LogPrefix + "Created directory 
{0}", dstDirectory);
                         }
 
+                       if (File.Exists(dstPath))
+                               File.SetAttributes(dstPath, FileAttributes.Normal);
                         File.Copy(sourcePath, dstPath, true);
                     } catch (Exception e) {
                         string msg = String.Format(CultureInfo.InvariantCulture, 
"Cannot copy {0} to {1}", sourcePath, dstPath);
Index: src/NAnt.Core/Tasks/LoopTask.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.Core/Tasks/LoopTask.cs,v
retrieving revision 1.1
diff -u -r1.1 LoopTask.cs
--- src/NAnt.Core/Tasks/LoopTask.cs     14 Aug 2002 23:21:51 -0000      1.1
+++ src/NAnt.Core/Tasks/LoopTask.cs     3 Mar 2003 17:34:34 -0000
@@ -27,7 +27,7 @@
     /// <remarks>
     ///   <para>Loop over items in a set. Can loop over files in directory, lines in 
a file, etc.</para>
     ///   <para>The Property value is stored before the loop is done, and restored 
when the loop is finished. 
-    ///   The Property is returned to its normal value once it is used.</para>
+    ///   The Property is returned to its normal value once it is used.  Read-only 
parameters cannot be overridden in this loop.</para>
     /// </remarks>
     /// <example>
     ///   <para>Loops over the files in C:\</para>
@@ -49,11 +49,19 @@
     ///   <para>Loops over a list</para>
     ///   <code>
     ///     <![CDATA[
-    /// <foreach item="String" in="1 2 3" delim=" " property="count">
+    /// <foreach item="String" in="1 2,3" delim=" ," property="count">
     ///     <echo message="${count}"/>
     /// </foreach>
     ///     ]]>
     ///   </code>
+    ///   <para>Loops over lines in the file "properties.csv", where each line is of 
the format name,value.</para>
+    ///   <code>
+    ///                <![CDATA[
+       ///     <foreach item="Line" in="properties.csv" delim="," property="x,y">
+       ///             <echo message="Read pair ${x}=${y}"/>
+       ///     </foreach>
+       ///             ]]>
+    ///   </code>
     /// </example>
     [TaskName("foreach")]
     public class LoopTask : TaskContainer {
@@ -64,20 +72,35 @@
             String,
             Line
         }
+               public enum TrimTypes {
+                       None,
+                       End,
+                       Start,
+                       Both
+               }
+
         string _prop = null;
+               string[] _props = null;
         ItemTypes _itemType = ItemTypes.None;
+               TrimTypes _trimType = TrimTypes.None;
         string _source = null;
-        string _delim = " ";
+        string _delim = null;
 
-        /// <summary>The NAnt propperty name that should be used for the current 
iterated item.</summary>
+        /// <summary>The NAnt propperty name(s) that should be used for the current 
iterated item.</summary>
+        /// <remarks>If specifying multiple properties, separate them with a 
comma.</remarks>
         [TaskAttribute("property", Required=true)]
         public string Property { 
             get { return _prop; } 
             set {
                 _prop = value;
-                if(Properties.IsReadOnlyProperty(_prop)) {
-                    throw new BuildException("Property is readonly! :" + _prop, 
Location); 
-                }
+                               _props = _prop.Split( ',' );
+                               foreach ( string prop in _props )
+                               {
+                                       if(Properties.IsReadOnlyProperty(prop)) 
+                                       {
+                                               throw new BuildException("Property is 
readonly! :" + prop, Location); 
+                                       }
+                               }
             }
         }
 
@@ -87,7 +110,13 @@
         [TaskAttribute("item", Required=true)]
         public ItemTypes ItemType   { get { return _itemType;} set {_itemType = 
value; }}
 
-        /// <summary>
+               /// <summary>
+               /// The type of whitespace trimming that should be done.
+               /// </summary>
+               [TaskAttribute("trim")]
+               public TrimTypes TrimType   { get { return _trimType;} set {_trimType 
= value; }}
+
+               /// <summary>
         /// The source of the iteration.
         /// </summary>
         [TaskAttribute("in", Required=true)]
@@ -100,7 +129,11 @@
         public string Delimiter { get { return _delim;} set {_delim = value; }}
 
         protected override void ExecuteTask() {
-            string oldPropVal = Properties[_prop];
+                       string[] oldPropVals = new string[ _props.Length ];
+                       // Save all of the old property values
+                       for ( int nIndex = 0; nIndex < oldPropVals.Length; nIndex++ ) {
+                               oldPropVals[ nIndex ] = Properties[ _props[ nIndex ] ];
+                       }
             
             try {
                 switch(ItemType) {
@@ -109,7 +142,9 @@
                     case ItemTypes.File: {
                         if(!Directory.Exists(Project.GetFullPath(_source)))
                             throw new BuildException("Invalid Source: " + _source, 
Location);
-                        DirectoryInfo dirInfo = new 
DirectoryInfo(Project.GetFullPath(_source));
+                                               if(_props.Length != 1)
+                                                       throw new 
BuildException(@"Only one property is valid for item=""File""");
+                                               DirectoryInfo dirInfo = new 
DirectoryInfo(Project.GetFullPath(_source));
                         FileInfo[] files = dirInfo.GetFiles();
                         foreach(FileInfo file in files) {
                             DoWork(file.FullName);
@@ -119,6 +154,8 @@
                     case ItemTypes.Folder: {
                         if(!Directory.Exists(Project.GetFullPath(_source)))
                             throw new BuildException("Invalid Source: " + _source, 
Location);
+                                               if(_props.Length != 1)
+                                                       throw new 
BuildException(@"Only one property is valid for item=""Folder""");
                         DirectoryInfo dirInfo = new 
DirectoryInfo(Project.GetFullPath(_source));
                         DirectoryInfo[] dirs = dirInfo.GetDirectories();
                         foreach(DirectoryInfo dir in dirs) {
@@ -129,37 +166,61 @@
                     case ItemTypes.Line: {
                         if(!File.Exists(Project.GetFullPath(_source)))
                             throw new BuildException("Invalid Source: " + _source, 
Location);
-                        StreamReader sr = File.OpenText(Project.GetFullPath(_source));
+                                               if(_props.Length > 1 && ( Delimiter == 
null || Delimiter.Length == 0 ) )
+                                                       throw new 
BuildException("Delimiter(s) must be specified if multiple properties are specified");
+
+                                               StreamReader sr = 
File.OpenText(Project.GetFullPath(_source));
                         while(true) {
                             string line = sr.ReadLine();
                             if (line ==null)
                                 break;
-                            DoWork(line);
+                                                       if (Delimiter == null || 
Delimiter.Length == 0)
+                                                               DoWork(line);
+                                                       else
+                                                               
DoWork(line.Split(Delimiter.ToCharArray()));
                         }
                         sr.Close();
                         break;
                     }
                     case ItemTypes.String: {
-                        if(Delimiter != null && Delimiter.Length > 0) {
-                            string[] items = 
_source.Split(Delimiter.ToCharArray()[0]);
-                            foreach(string s in items)
-                                DoWork(s);
-                        }
-                        else
-                            throw new BuildException("Invalid delim: " + _delim, 
Location);
+                                               if(_props.Length > 1)
+                                                       throw new 
BuildException(@"Only one property may be specified for item=""String""");
+                                               if(Delimiter == null || 
Delimiter.Length == 0)
+                                                       throw new 
BuildException(@"Delimiter must be specified for item=""String""");
+                        string[] items = _source.Split(Delimiter.ToCharArray());
+                        foreach(string s in items)
+                            DoWork(s);
                         break;
                     }
-                        
                 }
             }
             finally {
-                Properties[_prop] = oldPropVal;
+                               // Restore all of the old property values
+                               for ( int nIndex = 0; nIndex < oldPropVals.Length; 
nIndex++ ) {
+                                       Properties[ _props[ nIndex ] ] = oldPropVals[ 
nIndex ];
+                               }
             }
-
         }
 
-        protected virtual void DoWork(string propVal) {
-            Properties[_prop]= propVal;
+        protected virtual void DoWork(params string[] propVals) {
+                       for ( int nIndex = 0; nIndex < propVals.Length; nIndex++ ) {
+                               string propValue = propVals[ nIndex ];
+                               if ( nIndex >= _props.Length )
+                                       throw new BuildException("Too many items on 
line");
+                               switch (_trimType)
+                               {
+                                       case TrimTypes.Both:
+                                               propValue = propValue.Trim();
+                                               break;
+                                       case TrimTypes.Start:
+                                               propValue = propValue.TrimStart();
+                                               break;
+                                       case TrimTypes.End:
+                                               propValue = propValue.TrimEnd();
+                                               break;
+                               }
+                               Properties[ _props[ nIndex ] ] = propValue;
+                       }
             base.ExecuteTask();
         }
     }
Index: src/NAnt.Core.Tests/Tasks/LoopTest.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.Core.Tests/Tasks/LoopTest.cs,v
retrieving revision 1.2
diff -u -r1.2 LoopTest.cs
--- src/NAnt.Core.Tests/Tasks/LoopTest.cs       25 Oct 2002 10:18:09 -0000      1.2
+++ src/NAnt.Core.Tests/Tasks/LoopTest.cs       3 Mar 2003 17:34:34 -0000
@@ -36,7 +36,7 @@
         public void Test_Loop_String_Default_Delim() {
             string _xml = @"
                     <project>
-                        <foreach item='String' in='1,2,3,4' delim=',' 
property='count'>
+                        <foreach item='String' in='1,2,3,4;5' delim=';,' 
property='count'>
                             <echo message='Count:${count}'/>
                         </foreach>
                     </project>";
@@ -46,7 +46,8 @@
             Assertion.Assert(result.IndexOf("Count:2") != -1);
             Assertion.Assert(result.IndexOf("Count:3") != -1);
             Assertion.Assert(result.IndexOf("Count:4") != -1);
-        }
+                       Assertion.Assert(result.IndexOf("Count:5") != -1);
+               }
         
         [Test]
         public void Test_Loop_Files() {
@@ -79,16 +80,57 @@
 
                [Test]
         public void Test_Loop_Lines() {
-            string _xml = @"
-                    <project>
-                    <!-- Hello from inside -->
-                        <foreach item='Line' in='${nant.project.basedir}/test.build' 
property='line'>
-                            <echo message='Line:${line}'/>
-                        </foreach>
-                    </project>";
-            string result = RunBuild(_xml);
-            //Log.WriteLine(result);
-            Assertion.Assert(result.IndexOf("Hello") != -1);
+                       string strTempFile = 
CreateTempFile("looptest.loop_lines_test.txt");
+                       using ( StreamWriter sw = new StreamWriter( strTempFile ) )
+                       {
+                               sw.WriteLine( "x,y" );
+                               sw.WriteLine( "x2,y2  " );
+                               sw.WriteLine( "x3  ,y3" );
+                               sw.WriteLine( "x4,  y4" );
+                               sw.Close();
+
+                               string _xml = String.Format( @"
+                                               <project>
+                                               <!-- Hello from inside -->
+                                                       <foreach item='Line' 
delim=',;' trim='Both' in='{0}' property='x,y'>
+                                                               <echo 
message='|${{x}}=${{y}}|'/>
+                                                       </foreach>
+                                               </project>", strTempFile );
+                               string result = RunBuild(_xml);
+                               //Log.WriteLine(result);
+                               Assertion.Assert(result.IndexOf("|x=y|") != -1);
+                               Assertion.Assert(result.IndexOf("|x2=y2|") != -1);
+                               Assertion.Assert(result.IndexOf("|x3=y3|") != -1);
+                               Assertion.Assert(result.IndexOf("|x4=y4|") != -1);
+                       }
         }
-    }
+
+               [Test]
+               public void Test_Loop_Lines_No_Delim() 
+               {
+                       string strTempFile = 
CreateTempFile("looptest.loop_lines_test.txt");
+                       using ( StreamWriter sw = new StreamWriter( strTempFile ) )
+                       {
+                               sw.WriteLine( "x,y " );
+                               sw.WriteLine( "x2,y2  " );
+                               sw.WriteLine( "  x3  ,y3 " );
+                               sw.WriteLine( "  x4,  y4 " );
+                               sw.Close();
+
+                               string _xml = String.Format( @"
+                                               <project>
+                                               <!-- Hello from inside -->
+                                                       <foreach item='Line' 
trim='Start' in='{0}' property='x'>
+                                                               <echo 
message='|${{x}}|'/>
+                                                       </foreach>
+                                               </project>", strTempFile );
+                               string result = RunBuild(_xml);
+                               Log.WriteLine(result);
+                               Assertion.Assert(result.IndexOf("|x,y |") != -1);
+                               Assertion.Assert(result.IndexOf("|x2,y2  |") != -1);
+                               Assertion.Assert(result.IndexOf("|x3  ,y3 |") != -1);
+                               Assertion.Assert(result.IndexOf("|x4,  y4 |") != -1);
+                       }
+               }       
+       }
 }

Reply via email to