View Javadoc
1   /**
2    * From Apache Uima migration utils file at
3    * http://svn.apache.org/repos/asf/incubator/uima/uimaj/trunk/uimaj-tools/src/main/java/org/apache/uima
4    * /tools/migration/IbmUimaToApacheUima.java
5    */
6   package org.andromda.utils;
7   
8   import java.io.File;
9   import java.io.FileInputStream;
10  import java.io.FileOutputStream;
11  import java.io.IOException;
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.HashSet;
15  import java.util.List;
16  import java.util.Set;
17  import java.util.regex.Matcher;
18  import java.util.regex.Pattern;
19  import java.util.zip.ZipEntry;
20  import java.util.zip.ZipInputStream;
21  import java.util.zip.ZipOutputStream;
22  import org.apache.commons.io.FileUtils;
23  import org.apache.commons.io.filefilter.FileFilterUtils;
24  import org.apache.commons.io.filefilter.IOFileFilter;
25  import org.apache.commons.lang.StringUtils;
26  
27  /**
28   * Iterate down through a directory hierarchy, replace @andromda.whatever.whatever with the Java compliant andromda_whatever_whatever tagged
29   * value (attribute) format. Open .zip files if necessary.
30   * 
31   * @author RJF3
32   */
33  public class GlobalPatternFileReplace
34  {
35      private static List<Replacement> replacements = new ArrayList<Replacement>();
36      private static int MAX_FILE_SIZE = 10000000; // don't update files bigger than 10 MB
37      private static Set<String> extensions = new HashSet<String>();
38      private static int filesScanned = 0;
39      private static int filesModified = 0;
40      private static int fileReplacements = 0;
41  
42      /**
43       * 
44       */
45      public GlobalPatternFileReplace()
46      {
47          // TODO Auto-generated constructor stub
48      }
49  
50      /**
51       * @param args
52       * @throws IOException 
53       */
54      public static void main(String[] args) throws IOException
55      {
56          File file = new File("C:/workspaces/A34/andromda341/cartridges/andromda-spring/pom.xml");
57          // parse command line
58          String dir = null;
59          for (int i = 0; i < args.length; i++)
60          {
61              if (args[i].startsWith("-"))
62              {
63                  if (args[i].equals("-ext"))
64                  {
65                      if (i + 1 >= args.length)
66                      {
67                          printUsageAndExit();
68                      }
69                      parseCommaSeparatedList(args[++i], extensions);
70                  }
71                  else
72                  {
73                      System.err.println("Unknown switch " + args[i]);
74                      printUsageAndExit();
75                  }
76              }
77              else
78              {
79                  if (dir != null)
80                  {
81                      printUsageAndExit();
82                  }
83                  else
84                  {
85                      dir = args[i];
86                      file = new File(dir);
87                  }
88              }
89          }
90          /*if (dir == null)
91          {
92              //file = new File(".");
93              //System.out.println("Using default current directory: " + file.getCanonicalPath());
94              file = new File("C:/workspaces/A34/andromda341/cartridges/andromda-spring");
95              //printUsageAndExit();
96          }
97          if (file == null)
98          {
99              printUsageAndExit();
100             return;
101         }*/
102         if (extensions.isEmpty())
103         {
104             parseCommaSeparatedList("xml,java,xml.zip,vsl,vm", extensions);
105         }
106 
107         try
108         {
109             /*
110             replaceInFile(file); //read resource files //map from IBM UIMA package names to Apache UIMA package names
111             */
112             readMapping("packageMapping.txt", replacements, true);
113             //other string replacements readMapping("stringReplacements.txt", replacements, false);
114 
115             // from system property, get list of file extensions to exclude
116             // do the replacements
117             System.out.println("Migrating your files in " + file.getCanonicalPath());
118             replaceInAllFiles(file);
119         }
120         catch (IOException e)
121         {
122             // TODO Auto-generated catch block
123             e.printStackTrace();
124         }
125 
126         System.out.println("Migration complete.");
127         System.out.println("Scanned " + filesScanned + " files.  " + filesModified + " files modified.");
128     }
129 
130     /**
131      * 
132      */
133     private static void printUsageAndExit()
134     {
135         System.err.println("Usage: java " + GlobalPatternFileReplace.class.getName() + " <directory> [-ext <fileExtensions>]");
136         System.err.println("<fileExtensions> is a comma separated list of file extensions to process, e.g.: java,xml,properties");
137         System.err.println("\tUse a trailing comma to include files with no extension (meaning their name contains no dot)");
138         System.exit(1);
139     }
140 
141     /**
142      * Applies the necessary replacements to all files in the given directory. Subdirectories are processed recursively.
143      * Filters out SVN files and target directory by default.
144      * 
145      * @param dir directory containing files to replace
146      * @throws IOException if an I/O error occurs
147      */
148     private static void replaceInAllFiles(File dir) throws IOException
149     {
150         if (dir.isDirectory())
151         {
152             IOFileFilter fileFilter = FileFilterUtils.trueFileFilter();
153             IOFileFilter notTargetFileFilter = FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("target"));
154             IOFileFilter dirFilter = FileFilterUtils.and(FileFilterUtils.makeSVNAware(null), notTargetFileFilter);
155             Collection<File> files = FileUtils.listFiles(dir, fileFilter, dirFilter);
156             for (File file : files)
157             {
158                 if (file.isFile())
159                 {
160                     boolean process = false;
161                     // Check files against extensions filter
162                     for (String ext : extensions)
163                     {
164                         if (file.getName().endsWith("." + ext))
165                         {
166                             process = true;
167                             break;
168                         }
169                     }
170                     if (process)
171                     {
172                         // do the replacements
173                         // for .zip files, unzip, replace, and put back in archive
174                         if (file.getName().endsWith(".zip"))
175                         {
176                             replaceInArchive(file);
177                         }
178                         else
179                         {
180                             replaceInFile(file);
181                         }
182                     }
183                 }
184     
185                 // recursively call on subdirectories
186                 else if (file.isDirectory())
187                 {
188                     replaceInAllFiles(file);
189                 }
190             }
191         }
192         if (dir.isFile())
193         {
194             if (dir.getName().endsWith(".zip"))
195             {
196                 replaceInArchive(dir);
197             }
198             else
199             {
200                 replaceInFile(dir);
201             }
202         }
203     }
204 
205     /**
206      * Checks if file exists and is not excluded by extension and is writable.
207      * 
208      * @param file the file to process
209      */
210     private static boolean checkAttributes(File file) throws IOException
211     {
212         // skip files with extensions specified in the excludes list
213         if (!extensions.isEmpty())
214         {
215             String filename = file.getName();
216             String ext = "";
217             int lastDot = filename.lastIndexOf('.');
218             if (lastDot > -1)
219             {
220                 ext = filename.substring(lastDot + 1);
221             }
222             if (!extensions.contains(ext.toLowerCase()))
223             {
224                 return false;
225             }
226         }
227 
228         // make writable files that are readonly
229         if (!file.canRead())
230         {
231             //new FilePermission(file.getCanonicalPath(), "read,write,execute,delete");
232             //file.setReadable(true, false);
233             System.err.println("Can't read file: " + file.getCanonicalPath());
234             //continue;
235         }
236         /*if (!file.canWrite())
237         {
238             System.err.println("Skipping unwritable file: " + file.getCanonicalPath());
239             return false;
240         }*/
241         // skip files that are too big
242         if (file.length() > MAX_FILE_SIZE)
243         {
244             System.out.println("Skipping file " + file.getCanonicalPath() + " with size: " + file.length() + " bytes");
245             return false;
246         }
247 
248         return true;
249     }
250 
251     /**
252      * Applies replacements to a single file.
253      * 
254      * @param file the file to process
255      */
256     private static int replaceInArchive(File file) throws IOException
257     {
258         // do the replacements in each file in the archive
259         
260         int replacements = 0;
261         // for .zip files, unzip, replace, and put back in archive
262         if (file.isFile() && file.getName().endsWith(".zip"))
263         {
264             File location = unpack(file);
265             IOFileFilter fileFilter = FileFilterUtils.suffixFileFilter("xml");
266             IOFileFilter dirFilter = FileFilterUtils.makeCVSAware(null);
267             Collection<File> archiveFiles = FileUtils.listFiles(location, fileFilter, dirFilter);
268             for (File archiveFile : archiveFiles)
269             {
270                 // Only update zip archive contents if they have changed
271                 int replacement = replaceInFile(archiveFile);
272                 if (replacement > 0)
273                 {
274                     pack(file, archiveFile.getAbsolutePath());
275                     replacements += replacement;
276                 }
277                 archiveFile.delete();
278             }
279             location.delete();
280         }
281         return replacements;
282     }
283 
284     /**
285      * Applies replacements to a single file.
286      * 
287      * @param file the file to process
288      */
289     private static int replaceInFile(File file) throws IOException
290     {
291         // do the replacements
292         fileReplacements = 0;
293         if (!checkAttributes(file))
294         {
295             return fileReplacements;
296         }
297         String original;
298         try
299         {
300             original = FileUtils.readFileToString(file);
301         }
302         catch (IOException e)
303         {
304             System.err.println("Error reading " + file.getCanonicalPath());
305             System.err.println(e.getMessage());
306             return fileReplacements;
307         }
308         String contents = original;
309         contents = replaceInPattern(contents);
310 
311         // write file if it changed
312         if (!contents.equals(original))
313         {
314             if (!file.canWrite())
315             {
316                 // JDK 1.6 only. How do we make file writable in JDK 1.5?
317                 file.setReadable(true, false);
318                 System.out.println("Making file writable: " + file.getCanonicalPath());
319                 //new FilePermission(file.getCanonicalPath(), "read,write,execute,delete");               
320             }
321             if (!file.canWrite())
322             {
323                 System.err.println("Could not make file writable: " + file.getCanonicalPath());
324             }
325             else
326             {
327                 FileUtils.writeStringToFile(file, contents);
328                 filesModified++;
329             }
330             System.out.println(file.getAbsolutePath() + " " + fileReplacements + " replacements");
331         }
332         filesScanned++;
333 
334         // check for situations that may need manual attention,
335         // updates filesNeedingManualAttention field
336         // checkForManualAttentionNeeded(file, original);
337         return fileReplacements;
338     }
339 
340     /**
341      * Unpacks the archive file.
342      *
343      * @param file File to be unpacked.
344      * @return File directory where contents were unpacked
345      * @throws IOException 
346      */
347     protected static File unpack(
348         final File file)
349         throws IOException
350     {
351         return unpack(file, null);
352     }
353 
354     /**
355      * Unpacks the archive file.
356      *
357      * @param file File to be unpacked.
358      * @param location Location where to put the unpacked files.
359      * @return File directory where contents were unpacked
360      * @throws IOException 
361      */
362     protected static File unpack(
363         final File file,
364         File location)
365         throws IOException
366     {
367         final int ext = file.getAbsolutePath().lastIndexOf('.');
368         if (ext<1)
369         {
370             throw new IOException("File has no extension: " + file.getAbsolutePath());
371         }
372             
373         if (location==null || !location.exists())
374         {
375             location = new File(file.getParent() + "/" + StringUtils.remove(file.getName(), '.') + "/");
376         }
377         if (!location.getAbsolutePath().endsWith("/") && !location.getAbsolutePath().endsWith("\\"))
378         {
379             location = new File(location.getAbsolutePath() + "/");
380         }
381         if (!location.exists())
382         {
383             // Create the directory
384             FileUtils.forceMkdir(location);
385         }
386         //final String archiveExt = file.getAbsolutePath().substring(ext+1);
387         //final String archiveExt = FileUtils.getExtension(file.getAbsolutePath()).toLowerCase();
388         try
389         {
390             /*ZipFile zipFile = new ZipFile(file);
391             for (File fileEntry : zipFile.entries())
392             final UnArchiver unArchiver = GlobalPatternFileReplace.archiverManager.getUnArchiver(archiveExt);
393             unArchiver.setSourceFile(file);
394             unArchiver.setDestDirectory(location);
395             unArchiver.extract();
396             }
397         catch (Throwable throwable)
398         {
399             if (throwable instanceof IOException || throwable instanceof ArchiverException)
400             {
401                 throw new IOException("Error unpacking file: " + file + "to: " + location, throwable);
402             //}
403         }*/
404             byte[] buf = new byte[1024];
405             ZipInputStream zipinputstream = null;
406             ZipEntry zipentry;
407             zipinputstream = new ZipInputStream(new FileInputStream(file));
408     
409             zipentry = zipinputstream.getNextEntry();
410             while (zipentry != null) 
411             { 
412                 //for each entry to be extracted
413                 String entryName = zipentry.getName();
414                 //System.out.println("entryname "+entryName);
415                 FileOutputStream fileoutputstream;
416                 File newFile = new File(entryName);
417                 String directory = newFile.getParent();
418                 
419                 if(directory == null)
420                 {
421                     if(newFile.isDirectory())
422                         break;
423                 }
424                 
425                 File output = new File(location.getAbsolutePath(), entryName);
426                 fileoutputstream = new FileOutputStream(output);             
427     
428                 int n;
429                 while ((n = zipinputstream.read(buf, 0, 1024)) > -1)
430                     fileoutputstream.write(buf, 0, n);
431     
432                 fileoutputstream.close(); 
433                 zipinputstream.closeEntry();
434                 zipentry = zipinputstream.getNextEntry();
435     
436             }//while
437     
438             zipinputstream.close();
439         }
440         catch (Exception e)
441         {
442             e.printStackTrace();
443         }
444         return location;
445     }
446 
447     /**
448      * Writes the given archive file and includes
449      * the file given by the <code>path</code>
450      *
451      * @param zipArchive the zip archive.
452      * @param path the path of the file to write to the zip archive.
453      * @throws IOException
454      */
455     private static void pack(
456         final File zipArchive,
457         final String path)
458         throws IOException
459     {
460         // - retrieve the name of the file given by the path.
461         /*final String name = path.replaceAll(
462                 PATH_REMOVE_PATTERN,
463                 "");*/
464         //final String name = zipArchive.getName();
465         final ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipArchive));
466         File pathFile = new File(path);
467         final ZipEntry zipEntry = new ZipEntry(pathFile.getName());
468         zipEntry.setMethod(ZipEntry.DEFLATED);
469         zipOutputStream.putNextEntry(zipEntry);
470         final FileInputStream inputStream = new FileInputStream(path);
471         final byte[] buffer = new byte[1024];
472         int n = 0;
473         while ((n =
474                 inputStream.read(
475                     buffer,
476                     0,
477                     buffer.length)) > 0)
478         {
479             zipOutputStream.write(
480                 buffer,
481                 0,
482                 n);
483         }
484         inputStream.close();
485         zipOutputStream.closeEntry();
486         zipOutputStream.close();
487     }
488 
489     /**
490      * Replaces values within a regex match for each replacement in replacements list
491      * 
492      * @param contents File contents string
493      * @param replacement The pattern, replace, replaceWith, and deleteStr values
494      */
495     private static String replaceInPattern(String contents)
496     {
497         // apply replacements
498         for (Replacement replacement : replacements)
499         {
500             // contents = contents.replaceAll(replacement.regex, replacement.replacementStr);
501             contents = replaceInPattern(contents, replacement);
502         }
503         return contents;
504     }
505 
506     /**
507      * Replaces values within a regex match
508      * 
509      * @param contents File contents string
510      * @param replacement The pattern, replace, replaceWith, and deleteStr values
511      */
512     private static String replaceInPattern(String contents, Replacement replacement)
513     {
514         // StringBuffer contentBuffer = new StringBuffer(contents);
515         Matcher matcher = replacement.pattern.matcher(contents);
516         StringBuffer sb = new StringBuffer();
517         while (matcher.find())
518         {
519             boolean ignoreString = false;
520             String toReplace = contents.substring(matcher.start(), matcher.end());
521             for (String ignorePattern : replacement.ignorePatterns)
522             {
523                 if (toReplace.indexOf(ignorePattern) > -1)
524                 {
525                     ignoreString = true;
526                     break;
527                 }
528             }
529             if (!ignoreString)
530             {
531                 toReplace = toReplace.replace(replacement.replaceStr, replacement.replaceWithStr);
532                 toReplace = StringUtils.remove(toReplace, replacement.deleteStr);
533                 matcher.appendReplacement(sb, toReplace);
534                 fileReplacements++;
535             }
536         }
537         sb = matcher.appendTail(sb);
538         return sb.toString();
539     }
540 
541     /**
542      * Parses a comma separated list, entering each value into the results Collection. Trailing empty strings are included in the results
543      * Collection.
544      * 
545      * @param string string to parse
546      * @param results Collection to which each value will be added
547      */
548     private static Set<String> parseCommaSeparatedList(String string, Set<String> results)
549     {
550         String[] components = string.split(",", -1);
551         if (results==null)
552         {
553             results = new HashSet<String>();
554         }
555         for (int i = 0; i < components.length; i++)
556         {
557             results.add(components[i]);
558         }
559         return results;
560     }
561 
562     /**
563      * Reads a mapping from a resource file, and populates a List of Replacement objects. We don't use a Map because the order in which the
564      * replacements are applied can be important.
565      * 
566      * @param fileName
567      *            name of file to read from (looked up looking using Class.getResource())
568      * @param mappings
569      *            List to which Replacement objects will be added. Each object contains the regex to search for and the replacement string.
570      * @param treatAsPackageNames
571      *            if true, the keys in the resource file will be considered package names, and this routine will produce regexes that
572      *            replace any fully-qualified class name belonging to that package. Also in this case updates the static ibmPackageNames
573      *            HashSet.
574      */
575     private static void readMapping(String fileName, List mappings, boolean treatAsPackageNames) throws IOException
576     {
577         // Placeholder until txt file is built/read
578         //List<String> ignorePatterns = new ArrayList<String>();
579         //ignorePatterns.add(".cvs.");
580         // Find pattern @andromda. followed by lowercase letters followed by period (repeating groups) followed by lowercase letter
581         Replacement replacement = new Replacement("@andromda.([a-z]+[\\\\.])+[a-z]", ".", "_", "@", parseCommaSeparatedList(".cvs.", null));
582         GlobalPatternFileReplace.replacements.add(replacement);
583 
584         /*URL pkgListFile = AtAndromdaCleanup.class.getResource(fileName);
585         InputStream inStream = pkgListFile.openStream();
586         BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
587         String line = reader.readLine();
588         while (line != null)
589         {
590             String[] mapping = line.split(" ");
591             String regex, replaceStr;
592             if (treatAsPackageNames)
593             {
594                 // we do special processing for package names to try to handle the case where
595                 // user code exists in a package prefixed by com.ibm.uima.
596                 // We only replace the package name when it appears as part of a fully-qualified
597                 // class name in that package, not as a prefix of another package.
598 
599                 // turn package name into a regex (have to escape the . and,
600                 // technically, should allow whitepsace around dots)
601                 String pkgRegex = mapping[0].replaceAll("\\.", "\\\\s*\\\\.\\\\s*");
602                 // form regex that will find any fully-qualified class name in this package
603                 regex = pkgRegex + "(\\.(\\*|[A-Z]\\w*))";
604                 replaceStr = mapping[1] + "$1";
605                 // ibmPackageNames.add(mapping[0]);
606             }
607             else
608             {
609                 // form regex from src, by escaping dots and allowing whitespace
610                 regex = mapping[0].replaceAll("\\.", "\\\\s*\\\\.\\\\s*");
611                 replaceStr = mapping[1];
612             }
613 
614             // Regex for @andromda.([a-z]+[\.])+ matches @andromda. plus repeating sequence of lowercase letters followed by period
615             // replace . with _ in all occurrences within the matched pattern result String, remove @
616             List<String> ignorePatterns = new ArrayList<String>();
617             ignorePatterns.add(".cvs.");
618             Replacement replacement = new Replacement("@andromda.([a-z]+[\\\\.])+", ".", "_", "@", ignorePatterns);
619             // Replacement replacement = new Replacement(regex, replaceStr);
620             mappings.add(replacement);
621             line = reader.readLine();
622         }
623         inStream.close();*/
624     }
625 
626     /**
627      * Replace all occurrences of replacement String with replaceWithString inside regex result Delete all occurrences of deleteStr inside
628      * regex result
629      * 
630      * @author RJF3
631      */
632     private static class Replacement
633     {
634         String regex;
635         String deleteStr;
636         String replaceStr;
637         String replaceWithStr;
638         Set<String> ignorePatterns;
639         Pattern pattern;
640 
641         /**
642          * @param regexIn Input regular expression
643          * @param replacement String to replace text within the existing string
644          * @param replaceWithIn String to replace with
645          * @param deleteStrIn String to delete within the existing String
646          * @param ignorePatternsIn If one of the patterns exists in the existing String, skip replacements
647          */
648         Replacement(String regexIn, String replacement, String replaceWithIn, String deleteStrIn, Set<String> ignorePatternsIn)
649         {
650             this.regex = regexIn;
651             this.replaceStr = replacement;
652             this.deleteStr = deleteStrIn;
653             this.replaceWithStr = replaceWithIn;
654             this.ignorePatterns = ignorePatternsIn;
655             this.pattern = Pattern.compile(regex);
656         }
657     }
658 }