KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > idaremedia > antx > flowcontrol > call > ForEachTask


1 /**
2  * $Id: ForEachTask.java 180 2007-03-15 12:56:38Z ssmc $
3  * Copyright 2002-2004 iDare Media, Inc. All rights reserved.
4  *
5  * Originally written by iDare Media, Inc. for release into the public domain. This
6  * library, source form and binary form, is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your option) any later
9  * version.<p>
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU LGPL (GNU Lesser General Public License) for more details.<p>
14  *
15  * You should have received a copy of the GNU Lesser General Public License along with this
16  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite
17  * 330, Boston, MA 02111-1307 USA. The LGPL can be found online at
18  * http://www.fsf.org/copyleft/lesser.html<p>
19  *
20  * This product has been influenced by several projects within the open-source community.
21  * The JWare developers wish to acknowledge the open-source community's support. For more
22  * information regarding the open-source products used within JWare, please visit the
23  * JWare website.
24  *----------------------------------------------------------------------------------------*
25  * WEBSITE- http://www.jware.info EMAIL- inquiries@jware.info
26  *----------------------------------------------------------------------------------------*
27  **/

28
29 package com.idaremedia.antx.flowcontrol.call;
30
31 import java.io.BufferedReader JavaDoc;
32 import java.io.File JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.NoSuchElementException JavaDoc;
36
37 import org.apache.tools.ant.BuildException;
38 import org.apache.tools.ant.DirectoryScanner;
39 import org.apache.tools.ant.Project;
40 import org.apache.tools.ant.types.DirSet;
41 import org.apache.tools.ant.types.FileSet;
42 import org.apache.tools.ant.types.Path;
43 import org.apache.tools.ant.types.Reference;
44 import org.apache.tools.ant.util.FileUtils;
45
46 import com.idaremedia.antx.AntX;
47 import com.idaremedia.antx.AntXFixture;
48 import com.idaremedia.antx.FlexString;
49 import com.idaremedia.antx.flowcontrol.HardLimitEnabled;
50 import com.idaremedia.antx.helpers.ArrayIterator;
51 import com.idaremedia.antx.helpers.Empties;
52 import com.idaremedia.antx.helpers.Tk;
53 import com.idaremedia.antx.parameters.TrimEnabled;
54 import com.idaremedia.antx.starters.ListFriendly;
55
56 /**
57  * A caller extension that adds a looping mechanism for executing a set of steps or
58  * targets. This task is the internal implementation; only the common loop parameters
59  * are defined. Additional script-facing options must be provided by a subclass like
60  * {@linkplain CallForEachTask}. A special cursor property is passed to each step
61  * or target; this property's value contains the current iteration identifier (which
62  * is based on the kind-of loop control defined). This task is an extension of the
63  * simple {@linkplain OnceTask} that adds a looping decorator around
64  * the call-in-sequence functionality of the OnceTask.
65  *
66  * @since JWare/AntX 0.1
67  * @author ssmc, &copy;2002-2004 <a HREF="http://www.jware.info">iDare&nbsp;Media,&nbsp;Inc.</a>
68  * @version 0.5
69  * @.safety guarded
70  * @.group impl,infra
71  **/

72
73 public abstract class ForEachTask extends OnceTask implements TrimEnabled, HardLimitEnabled
74 {
75     /**
76      * Initialzies a new ForEachTask instance.
77      **/

78     protected ForEachTask()
79     {
80         super(AntX.flow+"foreach");
81     }
82
83
84     /**
85      * Initializes a new CV-labeled ForEachTask instance.
86      * @param iam CV-label (non-null)
87      **/

88     protected ForEachTask(String JavaDoc iam)
89     {
90         super(iam);
91     }
92
93
94     /**
95      * Sets this foreach task's cursor's property name. This cursor property is
96      * automatically passed to each step or target as a readonly system property
97      * (it's refreshed every iteration).
98      * @param cursorName cursor's property name (non-null)
99      **/

100     public void setCursor(String JavaDoc cursorName)
101     {
102         require_(!Tk.isWhitespace(cursorName),"setCursor- nonwspc cursor");
103         m_cursorName = cursorName;
104     }
105
106
107     /**
108      * Synonym for {@linkplain #setCursor setCursor}; convenient short-hand.
109      **/

110     public final void setI(String JavaDoc cursorName)
111     {
112         setCursor(cursorName);
113     }
114
115
116     /**
117      * Returns this task's cursor's property name. Will return <i>null</i>
118      * if never set.
119      **/

120     public String JavaDoc getCursorName()
121     {
122         return m_cursorName;
123     }
124
125
126 // ---------------------------------------------------------------------------------------------------------|
127
// Optional Hard Loop Stopper (Script-Facing)
128
// ---------------------------------------------------------------------------------------------------------|
129

130     /**
131      * Gives this loop task a hard limit to the number of times
132      * it can execute. This attribute serves mostly as an error
133      * handling mechanism to prevent infinite loops.
134      * @since JWare/AntX 0.5
135      **/

136     public void setMaxLoops(int maxLoops)
137     {
138         m_maxLoops = maxLoops>=0 ? maxLoops : Integer.MAX_VALUE;
139     }
140
141
142
143     /**
144      * Returns <i>true</i> if this loop tasks has a hard limit.
145      * @since JWare/AntX 0.5
146      **/

147     public final boolean hasMax()
148     {
149         return m_maxLoops!=Integer.MAX_VALUE;
150     }
151
152
153
154     /**
155      * Returns this loop tasks hard limit or <span class="src">-1</span>
156      * if no such limit exists.
157      * @since JWare/AntX 0.5
158      **/

159     public final int getMaxLoops()
160     {
161         return hasMax() ? m_maxLoops : -1;
162     }
163
164
165
166     /**
167      * Tells this tasks whether to fail if the hard limit is
168      * reached. Ignored if this loop task does not have a hard limit.
169      * @see #setMaxLoops setMaxLoops(LIMIT)
170      * @since JWare/AntX 0.5
171      **/

172     public void setHaltIfMax(boolean halt)
173     {
174         m_haltIfMax = halt ? Boolean.TRUE : Boolean.FALSE;
175     }
176
177
178
179     /**
180      * Returns this task's fail-if-max option setting. Will
181      * return <i>null</i> if never set explicitly. By default this
182      * task will <em>not</em> fail if its hard limit is hit; it
183      * just stops.
184      * @since JWare/AntX 0.5
185      **/

186     public final Boolean JavaDoc getHaltIfMax()
187     {
188         return m_haltIfMax;
189     }
190
191 //---------------------------------------------------------------------------------------------------------|
192
// Possible Loop-Controls (Script-Facing)
193
//---------------------------------------------------------------------------------------------------------|
194

195     /**
196      * Adds a list-based loop control declaration to this task.
197      * @param list the delimited-list of names (non-null)
198      * @see #setDelim
199      **/

200     public void setList(String JavaDoc list)
201     {
202         require_(list!=null,"setList- nonzro list");
203         m_byList = list;
204         resetCachedLoopInfo();
205     }
206
207
208     /**
209      * Returns this task's list-based loop control declaration. Returns
210      * <i>null</i> if never set.
211      **/

212     public final String JavaDoc getListLoopControl()
213     {
214         return m_byList;
215     }
216
217
218     /**
219      * Sets a custom list delimiter character. Used in conjunction
220      * with a list-based loop control declaration. Use the special
221      * <span class="src">[:space:]</span> delimiter to use the standard
222      * whitespace set of tokens (space, tab, newline, carriage-return).
223      * @param listDelim the delimiter string (non-null)
224      **/

225     public void setDelim(String JavaDoc listDelim)
226     {
227         require_(listDelim!=null && listDelim.length()>0,"setDelim- nonzro delim");
228         if ("[:space:]".equals(listDelim)) {
229             m_byListDelim = " \t\n\r";//NB:jdk standard-formfeed(AntX0.5)
230
} else {
231             m_byListDelim = listDelim;
232         }
233     }
234
235
236     /**
237      * Returns the list delimiter this task uses to parse list-based
238      * declarations. Never returns <i>null</i>; defaults to a comma (,).
239      **/

240     public final String JavaDoc getListDelim()
241     {
242         return m_byListDelim;
243     }
244
245
246     /**
247      * Adds a fileset-based loop control declaration to this task. The
248      * FileSet is not determined until this task is actually executed.
249      * @param filesetRef the refid of an existing fileset (non-null)
250      **/

251     public void setFiles(Reference filesetRef)
252     {
253         require_(filesetRef!=null && filesetRef.getRefId()!=null,"setfils- nonzro ref");
254         m_byFilesRef= filesetRef;
255         resetCachedLoopInfo();
256     }
257
258
259     /**
260      * Returns this task's fileset-based loop control declaration.
261      * Returns <i>null</i> if never set.
262      **/

263     public final Reference getFilesetLoopControl()
264     {
265         return m_byFilesRef;
266     }
267
268
269     /**
270      * Adds a dirset-based loop control declaration to this task.
271      * The DirSet is not determined until this task is actually executed.
272      * @param dirsetRef the refid of an existing dirset (non-null)
273      **/

274     public void setDirs(Reference dirsetRef)
275     {
276         require_(dirsetRef!=null && dirsetRef.getRefId()!=null,"setdirs- nonzro ref");
277         m_byDirsRef= dirsetRef;
278         resetCachedLoopInfo();
279     }
280
281
282     /**
283      * Returns this task's dirset-based loop control declaration.
284      * Returns <i>null</i> if never set.
285      **/

286     public final Reference getDirsetLoopControl()
287     {
288         return m_byDirsRef;
289     }
290
291
292     /**
293      * Adds a basename-only modifier to fileset based loops.
294      * @param only <i>true</i> if basename only
295      * @since JWare/AntX 0.4
296      **/

297     public void setBaseNameOnly(boolean only)
298     {
299         m_basenameOnly = only;
300     }
301
302
303     /**
304      * Adds a file-based loop control declaration to this task. The
305      * line-by-line contents of the file are interpreted as the items
306      * in a list to be iterated. Note unless explicitly changed,
307      * the entire line is passed as the cursor-- this task will do no
308      * automatic trimming or special interpretation of the line before
309      * passing it to the target.
310      * @param file path to an existing file (non-null)
311      * @see #setTrim
312      * @see #setIgnorePrefix
313      **/

314     public void setInFile(File JavaDoc file)
315     {
316         require_(file!=null,"setInFil- nonzro file");
317         m_byListFile = file;
318         resetCachedLoopInfo();
319     }
320
321
322     /**
323      * Returns this task's file-based loop control declaration.
324      * Returns <i>null</i> if never set.
325      **/

326     public final File JavaDoc getFileContentsLoopControl()
327     {
328         return m_byListFile;
329     }
330
331
332     /**
333      * Tells this task to trim lines returned from the file loop
334      * control.
335      * @since JWare/AntX 0.3
336      **/

337     public void setTrim(boolean trim)
338     {
339         m_byfileModifiers.setTrim(trim);
340     }
341
342
343     /**
344      * Returns <i>true</i> if this task will trim every line
345      * returned by the file loop control. Only used if a 'infile'
346      * loop-control defined.
347      * @since JWare/AntX 0.3
348      **/

349     public final boolean willTrim()
350     {
351         return m_byfileModifiers.isTrimmed();
352     }
353
354
355     /**
356      * Tells this task to ignore lines that begin with a particular
357      * comment character.
358      * @param prefix the prefix string
359      * @since JWare/AntX 0.3
360      **/

361     public void setIgnorePrefix(String JavaDoc prefix)
362     {
363         m_byfileModifiers.setIgnorePrefix(prefix);
364     }
365
366
367     /**
368      * Returns the prefix used to filter valid input lines for the
369      * 'infile' looping control. Returns <i>null</i> if never set
370      * or there is not special filter prefix. Only used if a 'infile'
371      * loop-control defined.
372      * @since JWare/AntX 0.3
373      **/

374     public final String JavaDoc getFileContentsIgnorePrefix()
375     {
376         return m_byfileModifiers.getPrefixFilter();
377     }
378
379
380     /**
381      * Adds a C-style scalar-range loop control declaration to
382      * this task. The string is a comma-delimited triple: <i>start</i>,
383      * <i>end+1</i>[,<i>increment</i>]. If unspecified the increment
384      * is set to one. The loop executes end-start/increment times; i.e
385      * it is exclusive of the <i>end</i>.
386      * @param loopDefn the defintion of the start,end,and increment (non-null)
387      **/

388     public void setIn(String JavaDoc loopDefn)
389     {
390         require_(loopDefn!=null,"setIn- nonzro lopvars");
391         m_byIn = loopDefn;
392         resetCachedLoopInfo();
393     }
394
395
396     /**
397      * Returns this task's scalar range loop control. Returns <i>null</i>
398      * if never set.
399      **/

400     public final String JavaDoc getForRangeLoopControl()
401     {
402         return m_byIn;
403     }
404
405
406
407     /**
408      * Adds a path-based loop control declaration to this task. The
409      * path's individual elements are passed as the cursor's value during
410      * the iteration.
411      * @param path the path list (non-null)
412      **/

413     public void setPathList(Path path)
414     {
415         require_(path!=null,"setpathlst- nonzro path");
416         m_byPath = path;
417         resetCachedLoopInfo();
418     }
419
420
421     /**
422      * Returns this task's path-based loop control. Returns <i>null</i>
423      * if never set.
424      **/

425     public final Path getPathListLoopControl()
426     {
427         return m_byPath;
428     }
429
430
431     /**
432      * Adds a pathref-based loop control declaration to this task. Exactly
433      * like {@linkplain #setPathList setPathList} but using a reference to
434      * an existing path declaration (instead of inlined like setPathList).
435      * @param pathRef the refid of an existing path (non-null)
436      **/

437     public void setPath(Reference pathRef)
438     {
439         require_(pathRef!=null && pathRef.getRefId()!=null,"setpathRef- nonzro ref");
440         m_byPathRef = pathRef;
441         resetCachedLoopInfo();
442     }
443
444
445     /**
446      * Returns this task's pathref-based loop control. Returns <i>null</i>
447      * if never set.
448      **/

449     public final Reference getPathLoopControl(Reference pathRef)
450     {
451         return m_byPathRef;
452     }
453
454
455     /**
456      * Adds a stringlist-based loop control declaration to this task.
457      * @param itemsRef reference to an existing list-friendly data object
458      * @since JWare/AntX 0.3
459      **/

460     public void setItems(Reference itemsRef)
461     {
462         require_(itemsRef!=null && itemsRef.getRefId()!=null,"setItms- nonzro ref");
463         m_byListRef = itemsRef;
464         resetCachedLoopInfo();
465     }
466
467
468     /**
469      * Returns this task's list-friendly loop control declaration.
470      * Returns <i>null</i> if never set.
471      * @since JWare/AntX 0.3
472      **/

473     public final Reference getItemsLoopControl()
474     {
475         return m_byListRef;
476     }
477
478
479     /**
480      * Resets this task's internal loop-control cached information.
481      **/

482     protected final void resetCachedLoopInfo()
483     {
484         if (m_foreachList!=null || m_byInStart!=Tk.NO_INT) {
485             m_foreachList= null;
486             m_byInStart= Tk.NO_INT;
487             m_byInEnd= Tk.NO_INT;
488             m_byInIncr= Tk.NO_INT;
489         }
490     }
491
492 //---------------------------------------------------------------------------------------------------------|
493
// Loop-Control -> Iterator Handlers
494
//---------------------------------------------------------------------------------------------------------|
495

496     private static final String JavaDoc BROKEN_CONTROL_MSG = "flow.foreach.err.bad.loopcontrol";
497     private static final String JavaDoc BROKEN_SETUP_MSG = "flow.foreach.invalid.setup";
498
499
500     /**
501      * Converts the '<i>list</i>' and '<i>items</i>' loop controls to an
502      * array of strings.
503      * @return <i>true</i> if list specified and parsed successfully
504      **/

505     protected boolean handleStringLists()
506     {
507         if (m_foreachList==null) {
508             if (m_byList!=null) {
509                 if (!Tk.isWhitespace(m_byList)) {
510                     List JavaDoc l= Tk.splitList(m_byList,m_byListDelim);
511                     if (!l.isEmpty()) {
512                         m_foreachList= (String JavaDoc[])l.toArray(Empties.EMPTY_STRING_ARRAY);
513                         log("Will do "+l.size()+" items for foreach(list)",Project.MSG_DEBUG);
514                     } else {
515                         String JavaDoc ermsg= uistrs().get(BROKEN_CONTROL_MSG,"list","");
516                         log(ermsg,Project.MSG_ERR);
517                     }
518                 } else {
519                     String JavaDoc ermsg= uistrs().get(BROKEN_CONTROL_MSG,"list","");
520                     log(ermsg,Project.MSG_WARN);
521                     m_foreachList = new String JavaDoc[0];
522                 }
523             }
524             else if (m_byListRef!=null) {
525                 Object JavaDoc o = getProject().getReference(m_byListRef.getRefId());
526                 if (!(o instanceof ListFriendly)) {
527                     String JavaDoc ermsg = getAntXMsg("task.bad.refid",m_byListRef.getRefId(),
528                                                ListFriendly.class.getName(),
529                                               (o==null ? "NULL" : o.getClass().getName()));
530                     log(ermsg,Project.MSG_ERR);
531                 } else {
532                     ListFriendly list = (ListFriendly)o;
533                     String JavaDoc[] sl= new String JavaDoc[list.size()];
534                     if (sl.length>0) {
535                         int i=0;
536                         Iterator JavaDoc itr= list.readonlyStringIterator(getProject());
537                         while (itr.hasNext()) {
538                             verify_(i<sl.length,"handlLst- no-concur-mods on roItr");
539                             sl[i++] = itr.next().toString();
540                         }
541                     }
542                     m_foreachList = sl;
543                     log("Will do "+sl.length+" items for foreach(items)",Project.MSG_DEBUG);
544                 }
545             }
546         }
547         return m_foreachList!=null;
548     }
549
550
551     /**
552      * Converts the '<i>in</i>' loop controls to a set of numeric loop controls.
553      * @return <i>true</i> if 'in' parameters parsed and converted successfully
554      **/

555     protected boolean handleIn()
556     {
557         if (m_byInStart==Tk.NO_INT) {
558             if (!Tk.isWhitespace(m_byIn)) {
559                 List JavaDoc l= Tk.splitList(m_byIn,",");
560                 int istart=Tk.NO_INT,iend=Tk.NO_INT,iincr=Tk.NO_INT;
561                 if (l.size()>=2) {
562                     istart= Tk.integerFrom(l.get(0),Tk.NO_INT);
563                     iend= Tk.integerFrom(l.get(1),Tk.NO_INT);
564                 }
565                 if (l.size()>=3) {
566                     iincr = Tk.integerFrom(l.get(2),Tk.NO_INT);
567                     if (iincr==Tk.NO_INT) {
568                         String JavaDoc ermsg= uistrs().get("flow.foreach.err.in.incr",l.get(2));
569                         log(ermsg,Project.MSG_ERR);
570                         return false;
571                     }
572                 }
573                 if (istart!=Tk.NO_INT && iend!=Tk.NO_INT) {
574                     m_byInStart = istart;
575                     m_byInEnd = iend;
576                     if (iincr!=Tk.NO_INT) {
577                         m_byInIncr= iincr;
578                     } else {
579                         m_byInIncr= 1;
580                     }
581                     int N = (iend-istart)/iincr;
582                     log("Will do "+N+" iterations for foreach(in)",Project.MSG_DEBUG);
583                 } else {
584                     String JavaDoc ermsg= uistrs().get(BROKEN_CONTROL_MSG,"in","");
585                     log(ermsg,Project.MSG_ERR);
586                 }
587             }
588         }
589         return m_byInStart!=Tk.NO_INT;
590     }
591
592
593     /**
594      * Converts the '<i>files</i>' loop controls to a set of string file names.
595      * @return <i>true</i> if fileset specified and converted successfully
596      **/

597     protected boolean handleFiles()
598     {
599         if (m_foreachList==null) {
600             if (m_byFilesRef!=null) {
601                 try {
602                     FileSet fs = new FileSet();
603                     fs.setProject(getProject());
604                     fs.setRefid(m_byFilesRef);
605                     DirectoryScanner dirscan = fs.getDirectoryScanner(getProject());
606                     m_foreachList= dirscan.getIncludedFiles();
607                     resolveAllFilesOrDirs(m_foreachList, dirscan);
608                     log("Will do "+m_foreachList.length+" files for foreach(files)",Project.MSG_DEBUG);
609                 } catch(BuildException bx) {
610                     String JavaDoc ermsg= uistrs().get(BROKEN_CONTROL_MSG,"files",bx.getMessage());
611                     log(ermsg,Project.MSG_ERR);
612                 }
613             }
614         }
615         return m_foreachList!=null;
616     }
617
618
619     /**
620      * Converts the '<i>dirs</i>' loop controls to a set of string directory names.
621      * @return <i>true</i> if dirset specified and converted successfully
622      **/

623     protected boolean handleDirs()
624     {
625         if (m_foreachList==null) {
626             if (m_byDirsRef!=null) {
627                 try {
628                     DirSet ds = new DirSet();
629                     ds.setProject(getProject());
630                     ds.setRefid(m_byDirsRef);
631                     DirectoryScanner dirscan = ds.getDirectoryScanner(getProject());
632                     m_foreachList= dirscan.getIncludedDirectories();
633                     resolveAllFilesOrDirs(m_foreachList, dirscan);
634                     log("Will do "+m_foreachList.length+" dirs for foreach(dirs)",Project.MSG_DEBUG);
635                 } catch(BuildException bx) {
636                     String JavaDoc ermsg = uistrs().get(BROKEN_CONTROL_MSG,"dirs",bx.getMessage());
637                     log(ermsg,Project.MSG_ERR);
638                 }
639             }
640         }
641         return m_foreachList!=null;
642     }
643
644
645     /**
646      * Converts the '<i>path</i>' and '<i>pathlist</i>' loop controls
647      * to a set of strings.
648      * @return <i>true</i> if path controls parsed and converted successfully
649      **/

650     protected boolean handlePaths()
651     {
652         if (m_foreachList==null) {
653             if (m_byPath!=null) {
654                 try {
655                     m_foreachList = m_byPath.list();
656                     log("Will do "+m_foreachList.length+" elements for foreach(path)",Project.MSG_DEBUG);
657                 } catch(BuildException bx) {
658                     String JavaDoc ermsg = uistrs().get(BROKEN_CONTROL_MSG,"path",bx.getMessage());
659                     log(ermsg,Project.MSG_ERR);
660                 }
661             }
662             else if (m_byPathRef!=null) {
663                 try {
664                     Path path = new Path(getProject());
665                     path.setRefid(m_byPathRef);
666                     m_foreachList = path.list();
667                     log("Will do "+m_foreachList.length+" elements for foreach(pathref)",Project.MSG_DEBUG);
668                 } catch(BuildException bx) {
669                     String JavaDoc ermsg = uistrs().get(BROKEN_CONTROL_MSG,"pathref",bx.getMessage());
670                     log(ermsg,Project.MSG_ERR);
671                 }
672             }
673         }
674         return m_foreachList!=null;
675     }
676
677
678     /**
679      * Converts the '<i>infile</i>' loop control to a set of strings.
680      * @return <i>true</i> if file specified and read successfully
681      **/

682     protected boolean handleFileContents()
683     {
684         if (m_foreachList==null) {
685             if (m_byListFile!=null && m_byListFile.canRead()) {
686                 try {
687                     BufferedReader JavaDoc fr = new BufferedReader JavaDoc(new java.io.FileReader JavaDoc(m_byListFile));
688                     List JavaDoc propList = AntXFixture.newList(10);
689                     Project P= getProject();
690                     String JavaDoc next;
691                     FlexString filter = (FlexString)m_byfileModifiers.clone();
692                     while ((next=fr.readLine())!=null) {
693                         filter.set(next);
694                         next = filter.getValue();
695                         if (next!=null) {
696                             propList.add(Tk.resolveString(P,next));
697                         }
698                     }
699                     m_foreachList = (String JavaDoc[])propList.toArray(Empties.EMPTY_STRING_ARRAY);
700                     log("Will do "+m_foreachList.length+" items for foreach(infile)",Project.MSG_DEBUG);
701                     propList = null;
702                 } catch(Exception JavaDoc ioxOrbuildX) {
703                     String JavaDoc ermsg = uistrs().get(BROKEN_CONTROL_MSG,"infile",ioxOrbuildX.getMessage());
704                     log(ermsg,Project.MSG_ERR);
705                 }
706             }
707         }
708         return m_foreachList!=null;
709     }
710
711
712     /**
713      * Ensures all file names from a directory scanner are
714      * fully-resolved absolute path names.
715      **/

716     private void resolveAllFilesOrDirs(String JavaDoc[] filesOrDirs, DirectoryScanner dirscan)
717     {
718         FileUtils fileUtils = AntXFixture.fileUtils();
719         File JavaDoc wrt = dirscan.getBasedir();
720         for (int i=0;i<filesOrDirs.length;i++) {
721             File JavaDoc rf= fileUtils.resolveFile(wrt,filesOrDirs[i]);
722             if (m_basenameOnly) {
723                 filesOrDirs[i]= rf.getName();
724             } else {
725                 filesOrDirs[i]= rf.getAbsolutePath();
726             }
727         }
728     }
729
730
731     /**
732      * Returns the underlying array of strings to be iterated <em>if</em>
733      * such a list is available. Will return <i>null</i> if this task
734      * never executed or the loop control doesn't convert to a simple
735      * string array.
736      **/

737     protected final String JavaDoc[] getForEachList()
738     {
739         return m_foreachList;
740     }
741
742
743     /**
744      * Sets the (dominant) string array to be iterated. Usually called from
745      * one of the 'handle' methods after the loop control has been converted
746      * to a concrete list of strings.
747      **/

748     protected final void setForEachList(String JavaDoc[] newlist)
749     {
750         m_foreachList = newlist;
751     }
752
753
754 //---------------------------------------------------------------------------------------------------------|
755
// Loop Execution
756
//---------------------------------------------------------------------------------------------------------|
757

758     /**
759      * Returns <i>true</i> if at least one loop control
760      * is defined.
761      **/

762     protected final boolean anyLoopControlsSpecified()
763     {
764         return (m_byList!=null || m_byListRef!=null ||
765                 m_byFilesRef!=null || m_byDirsRef!=null ||
766                 m_byListFile!=null || m_byPath!=null ||
767                 m_byPathRef!=null || m_byIn!=null
768                 );
769     }
770
771
772     /**
773      * Returns the 'effective' number of loop controls defined
774      * for this task. Currently only one kind is allowed.
775      **/

776     protected final int getNumberLoopControlsSpecified()
777     {
778         int N=0;
779         if (m_byList!=null || m_byListRef!=null) { N++; }//both=1
780
if (m_byIn!=null) { N++; }
781         if (m_byFilesRef!=null) { N++; }
782         if (m_byDirsRef!=null) { N++; }
783         if (m_byListFile!=null) { N++; }
784         if (m_byPath!=null || m_byPathRef!=null) { N++; }//both=1
785
return N;
786     }
787
788
789     /**
790      * Ensures some form of loop control and cursor has been defined
791      * for this task. Also verifies that all the inherited requirements
792      * have been met.
793      * @throws BuildException if none/too-many loop controls or no
794      * cursor defined or other inherited prerequiste violated
795      **/

796     protected void verifyCanExecute_(String JavaDoc calr)
797         throws BuildException
798     {
799         super.verifyCanExecute_(calr);
800
801         int Nc= getNumberLoopControlsSpecified();
802
803         if ((getCursorName()==null) || //required for steps
804
(Nc==0) || (Nc>1)) { //only one loop-control
805

806             String JavaDoc ermsg = uistrs().get(BROKEN_SETUP_MSG);
807             log(ermsg,Project.MSG_ERR);
808             throw new BuildException(ermsg,getLocation());
809         }
810     }
811
812
813     /**
814      * Executes this task's set of steps or targets for each element
815      * returned by an iterator. The iterator's value is passed to
816      * target using this task's cursor name setting-- the iterator's
817      * value is always converted to a string using the standard
818      * <span class="src">toString</span> method.
819      * @param itr iterator (non-null)
820      **/

821     protected final void forEachRunTargetCallers(Iterator JavaDoc itr)
822         throws BuildException
823     {
824         final String JavaDoc cursorName = getCursorName();
825         int ith=0;
826         final int Nth=m_maxLoops;
827
828         while (itr.hasNext() && ith<Nth) {
829             final String JavaDoc cursor = itr.next().toString();
830             try {
831                 runTargetCallers(new TargetCaller.RunSelector() {
832                         public void run(TargetCaller caller) {
833                             caller.run(cursorName, cursor);
834                         }
835                     });
836                 ith++;
837             } catch(RuntimeException JavaDoc rtX) {
838                 log(uistrs().get("flow.cursor.failure",cursor),
839                     Project.MSG_ERR);
840                 throw rtX;
841             }
842         }
843
844         if (ith==Nth) {
845             String JavaDoc msg = getMsg();
846             if (Tk.isWhitespace(msg)) {
847                 msg = getAntXMsg("flow.loop.overflow",String.valueOf(ith));
848             }
849             if (m_haltIfMax==Boolean.TRUE) {
850                 log(msg, Project.MSG_ERR);
851                 throw new BuildException(msg, getLocation());
852             }
853             log(msg, Project.MSG_INFO);
854         }
855     }
856
857
858     /**
859      * Determines what we're actually iterating over based on one of the
860      * kinds-of loop controls then iterates calling the targetted steps
861      * or targets for each iteration. All but the 'in' control produces a
862      * list of strings for iteration. An iterator is used to mask the difference
863      * between the 'in' control and all others. This iterator is passed to
864      * {@linkplain #forEachRunTargetCallers(Iterator)
865      * forEachRunTargetCallers(Iterator)}.
866      * @throws BuildException if unable to create a list for iteration
867      * @see IntRangeIterator
868      **/

869     protected void forEachRunTargetCallers() throws BuildException
870     {
871         if (!handleStringLists() && /*ugly-but-works*/
872             !handleIn() &&
873             !handleFileContents() &&
874             !handleFiles() &&
875             !handlePaths() &&
876             !handleDirs()) {
877
878             String JavaDoc ermsg = uistrs().get(BROKEN_SETUP_MSG);
879             log(ermsg,Project.MSG_ERR);
880             throw new BuildException(ermsg,getLocation());
881         }
882
883         if (m_foreachList!=null) {
884             forEachRunTargetCallers(new ArrayIterator(m_foreachList));
885         }
886         else if (m_byInStart!=Tk.NO_INT) {
887             forEachRunTargetCallers
888                 (new IntRangeIterator(m_byInStart,m_byInEnd,m_byInIncr));
889         }
890         else {
891             String JavaDoc ermsg = uistrs().get(BROKEN_SETUP_MSG);
892             log(ermsg,Project.MSG_ERR);
893             throw new BuildException(ermsg,getLocation());
894         }
895     }
896
897
898     /**
899      * For each loop iteration generates a series of partitioned calls to
900      * either a set of real top-level targets or a set of nested steps.
901      * Both kinds of calls actually execute a set of &lt;antcall&gt;
902      * helpers to do most of the work.
903      **/

904     public void execute() throws BuildException
905     {
906         verifyCanExecute_("execute");
907         forEachRunTargetCallers();
908     }
909
910
911     /**
912      * Iterator of a range of integers. Used with 'in' loop controls.
913      * @since JWare/AntX 0.1
914      * @author ssmc, &copy;2002-2003 <a HREF="http://www.jware.info">iDare&nbsp;Media,&nbsp;Inc.</a>
915      * @version 0.5
916      * @.safety single
917      **/

918     public static final class IntRangeIterator implements Iterator JavaDoc
919     {
920         private final int end, incr;
921         private int i;
922         public IntRangeIterator(int start, int end, int incr) {
923             this.i = start;
924             this.end= end;
925             this.incr = incr;
926         }
927         public void remove() {
928             throw new UnsupportedOperationException JavaDoc();
929         }
930         public boolean hasNext() {
931             return i<end;
932         }
933         public Object JavaDoc next() {
934             if (i<end) {
935                 Object JavaDoc iObject= String.valueOf(i);
936                 i += incr;
937                 return iObject;
938             }
939             throw new NoSuchElementException JavaDoc();
940         }
941     }
942
943 //---------------------------------------------------------------------------------------------------------|
944

945     //Required cursor name as property
946
private String JavaDoc m_cursorName;
947
948     //Required actual loop-controls is either
949
private String JavaDoc[] m_foreachList;//this-or
950
private int m_byInStart=Tk.NO_INT, m_byInEnd=Tk.NO_INT, m_byInIncr=Tk.NO_INT;//this
951

952     //Useful ways to specify loop-controls
953
private String JavaDoc m_byList, m_byListDelim= AntX.DEFAULT_DELIMITER;
954     private String JavaDoc m_byIn;
955     private Reference m_byFilesRef, m_byDirsRef, m_byPathRef;
956     private File JavaDoc m_byListFile;
957     private FlexString m_byfileModifiers = new FlexString();
958     private Path m_byPath;
959     private Reference m_byListRef;
960     private boolean m_basenameOnly;
961     
962     //Optional hard-brake for runaway loops (AntX0.5)
963
private int m_maxLoops = Integer.MAX_VALUE;
964     private Boolean JavaDoc m_haltIfMax = null;//NB: => no
965
}
966
967 /* end-of-ForEachTask.java */
968
Popular Tags