View Javadoc
1   package org.apache.maven.shared.utils.io;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Stack;
26  
27  /**
28   * DirectoryWalker
29   * 
30   * @version $Id: DirectoryWalker.html 955072 2015-06-16 23:06:34Z tibordigana $
31   */
32  class DirectoryWalker
33  {
34      /**
35       * DirStackEntry is an Item on the {@link DirectoryWalker#dirStack}
36       */
37      static class DirStackEntry
38      {
39          /**
40           * Count of files in the directory.
41           */
42          private final int count;
43  
44          /**
45           * Current Directory.
46           */
47          private final File dir;
48  
49          /**
50           * Index (or offset) within the directory count.
51           */
52          private int index;
53  
54          /**
55           * Offset for percentage calculations. Based on parent DirStackEntry.
56           */
57          private double percentageOffset;
58  
59          /**
60           * Size of percentage space to work with.
61           */
62          private double percentageSize;
63  
64          /**
65           * Create a DirStackEntry.
66           * 
67           * @param d the directory to track
68           * @param length the length of entries in the directory.
69           */
70          public DirStackEntry( File d, int length )
71          {
72              dir = d;
73              count = length;
74          }
75  
76          /**
77           * Calculate the next percentage offset. Used by the next DirStackEntry.
78           * 
79           * @return the value for the next percentage offset.
80           */
81          public double getNextPercentageOffset()
82          {
83              return percentageOffset + ( index * ( percentageSize / count ) );
84          }
85  
86          /**
87           * Calculate the next percentage size. Used by the next DirStackEntry.
88           * 
89           * @return the value for the next percentage size.
90           */
91          public double getNextPercentageSize()
92          {
93              return ( percentageSize / count );
94          }
95  
96          /**
97           * The percentage of the DirStackEntry right now. Based on count, index, percentageOffset, and percentageSize.
98           * 
99           * @return the percentage right now.
100          */
101         public int getPercentage()
102         {
103             double percentageWithinDir = (double) index / (double) count;
104             return (int) Math.floor( percentageOffset + ( percentageWithinDir * percentageSize ) );
105         }
106 
107         public String toString()
108         {
109             return "DirStackEntry[" + "dir=" + dir.getAbsolutePath() + ",count=" + count + ",index=" + index
110                 + ",percentageOffset=" + percentageOffset + ",percentageSize=" + percentageSize + ",percentage()="
111                 + getPercentage() + ",getNextPercentageOffset()=" + getNextPercentageOffset()
112                 + ",getNextPercentageSize()=" + getNextPercentageSize() + "]";
113         }
114     }
115 
116     private File baseDir;
117 
118     private int baseDirOffset;
119 
120     private Stack<DirStackEntry> dirStack;
121 
122     private final List<String> excludes;
123 
124     private final List<String> includes;
125 
126     private final List<DirectoryWalkListener> listeners;
127 
128     public DirectoryWalker()
129     {
130         this.includes = new ArrayList<String>();
131         this.excludes = new ArrayList<String>();
132         this.listeners = new ArrayList<DirectoryWalkListener>();
133     }
134 
135     public void addDirectoryWalkListener( DirectoryWalkListener listener )
136     {
137         this.listeners.add( listener );
138     }
139 
140     void addExclude( String exclude )
141     {
142         this.excludes.add( fixPattern( exclude ) );
143     }
144 
145     void addInclude( String include )
146     {
147         this.includes.add( fixPattern( include ) );
148     }
149 
150     /**
151      * Add's to the Exclude List the default list of SCM excludes.
152      */
153     public void addSCMExcludes()
154     {
155         String scmexcludes[] = DirectoryScanner.DEFAULTEXCLUDES;
156         for ( String scmexclude : scmexcludes )
157         {
158             addExclude( scmexclude );
159         }
160     }
161 
162     private void fireStep( File file )
163     {
164         DirStackEntry dsEntry = dirStack.peek();
165         int percentage = dsEntry.getPercentage();
166         for ( DirectoryWalkListener listener : this.listeners )
167         {
168             listener.directoryWalkStep( percentage, file );
169         }
170     }
171 
172     private void fireWalkFinished()
173     {
174         for ( Object listener1 : this.listeners )
175         {
176             DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
177             listener.directoryWalkFinished();
178         }
179     }
180 
181     private void fireWalkStarting()
182     {
183         for ( Object listener1 : this.listeners )
184         {
185             DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
186             listener.directoryWalkStarting( this.baseDir );
187         }
188     }
189 
190     private void fireDebugMessage( String message )
191     {
192         for ( Object listener1 : this.listeners )
193         {
194             DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
195             listener.debug( message );
196         }
197     }
198 
199     private String fixPattern( String pattern )
200     {
201         String cleanPattern = pattern;
202 
203         if ( File.separatorChar != '/' )
204         {
205             cleanPattern = cleanPattern.replace( '/', File.separatorChar );
206         }
207 
208         if ( File.separatorChar != '\\' )
209         {
210             cleanPattern = cleanPattern.replace( '\\', File.separatorChar );
211         }
212 
213         return cleanPattern;
214     }
215 
216     private boolean isExcluded( String name )
217     {
218         return isMatch( this.excludes, name );
219     }
220 
221     private boolean isIncluded( String name )
222     {
223         return isMatch( this.includes, name );
224     }
225 
226     private boolean isMatch( List<String> patterns, String name )
227     {
228         for ( String pattern : patterns )
229         {
230             boolean caseSensitive = true;
231             if ( SelectorUtils.matchPath( pattern, name, caseSensitive ) )
232             {
233                 return true;
234             }
235         }
236 
237         return false;
238     }
239 
240     private String relativeToBaseDir( File file )
241     {
242         return file.getAbsolutePath().substring( baseDirOffset + 1 );
243     }
244 
245     /**
246      * Performs a Scan against the provided {@link #setBaseDir(File)}
247      */
248     public void scan()
249     {
250         if ( baseDir == null )
251         {
252             throw new IllegalStateException( "Scan Failure.  BaseDir not specified." );
253         }
254 
255         if ( !baseDir.exists() )
256         {
257             throw new IllegalStateException( "Scan Failure.  BaseDir does not exist." );
258         }
259 
260         if ( !baseDir.isDirectory() )
261         {
262             throw new IllegalStateException( "Scan Failure.  BaseDir is not a directory." );
263         }
264 
265         if ( this.includes.isEmpty() )
266         {
267             // default to include all.
268             addInclude( "**" );
269         }
270 
271         fireWalkStarting();
272         dirStack = new Stack<DirStackEntry>();
273         scanDir( this.baseDir );
274         fireWalkFinished();
275     }
276 
277     private void scanDir( File dir )
278     {
279         File files[] = dir.listFiles();
280 
281         if ( files == null )
282         {
283             return;
284         }
285 
286         DirStackEntry curStackEntry = new DirStackEntry( dir, files.length );
287         if ( dirStack.isEmpty() )
288         {
289             curStackEntry.percentageOffset = 0;
290             curStackEntry.percentageSize = 100;
291         }
292         else
293         {
294             DirStackEntry previousStackEntry = dirStack.peek();
295             curStackEntry.percentageOffset = previousStackEntry.getNextPercentageOffset();
296             curStackEntry.percentageSize = previousStackEntry.getNextPercentageSize();
297         }
298 
299         dirStack.push( curStackEntry );
300 
301         for ( int idx = 0; idx < files.length; idx++ )
302         {
303             curStackEntry.index = idx;
304             String name = relativeToBaseDir( files[idx] );
305 
306             if ( isExcluded( name ) )
307             {
308                 fireDebugMessage( name + " is excluded." );
309                 continue;
310             }
311 
312             if ( files[idx].isDirectory() )
313             {
314                 scanDir( files[idx] );
315             }
316             else
317             {
318                 if ( isIncluded( name ) )
319                 {
320                     fireStep( files[idx] );
321                 }
322             }
323         }
324 
325         dirStack.pop();
326     }
327 
328     /**
329      * @param baseDir The baseDir to set.
330      */
331     public void setBaseDir( File baseDir )
332     {
333         this.baseDir = baseDir;
334         this.baseDirOffset = baseDir.getAbsolutePath().length();
335     }
336 
337 }