001/** 002 * From Apache Uima migration utils file at 003 * http://svn.apache.org/repos/asf/incubator/uima/uimaj/trunk/uimaj-tools/src/main/java/org/apache/uima 004 * /tools/migration/IbmUimaToApacheUima.java 005 */ 006package org.andromda.utils; 007 008import java.io.File; 009import java.io.FileInputStream; 010import java.io.FileOutputStream; 011import java.io.IOException; 012import java.util.ArrayList; 013import java.util.Collection; 014import java.util.HashSet; 015import java.util.List; 016import java.util.Set; 017import java.util.regex.Matcher; 018import java.util.regex.Pattern; 019import java.util.zip.ZipEntry; 020import java.util.zip.ZipInputStream; 021import java.util.zip.ZipOutputStream; 022import org.apache.commons.io.FileUtils; 023import org.apache.commons.io.filefilter.FileFilterUtils; 024import org.apache.commons.io.filefilter.IOFileFilter; 025import org.apache.commons.lang.StringUtils; 026 027/** 028 * Iterate down through a directory hierarchy, replace @andromda.whatever.whatever with the Java compliant andromda_whatever_whatever tagged 029 * value (attribute) format. Open .zip files if necessary. 030 * 031 * @author RJF3 032 */ 033public class GlobalPatternFileReplace 034{ 035 private static List<Replacement> replacements = new ArrayList<Replacement>(); 036 private static int MAX_FILE_SIZE = 10000000; // don't update files bigger than 10 MB 037 private static Set<String> extensions = new HashSet<String>(); 038 private static int filesScanned = 0; 039 private static int filesModified = 0; 040 private static int fileReplacements = 0; 041 042 /** 043 * 044 */ 045 public GlobalPatternFileReplace() 046 { 047 // TODO Auto-generated constructor stub 048 } 049 050 /** 051 * @param args 052 * @throws IOException 053 */ 054 public static void main(String[] args) throws IOException 055 { 056 File file = new File("C:/workspaces/A34/andromda341/cartridges/andromda-spring/pom.xml"); 057 // parse command line 058 String dir = null; 059 for (int i = 0; i < args.length; i++) 060 { 061 if (args[i].startsWith("-")) 062 { 063 if (args[i].equals("-ext")) 064 { 065 if (i + 1 >= args.length) 066 { 067 printUsageAndExit(); 068 } 069 parseCommaSeparatedList(args[++i], extensions); 070 } 071 else 072 { 073 System.err.println("Unknown switch " + args[i]); 074 printUsageAndExit(); 075 } 076 } 077 else 078 { 079 if (dir != null) 080 { 081 printUsageAndExit(); 082 } 083 else 084 { 085 dir = args[i]; 086 file = new File(dir); 087 } 088 } 089 } 090 /*if (dir == null) 091 { 092 //file = new File("."); 093 //System.out.println("Using default current directory: " + file.getCanonicalPath()); 094 file = new File("C:/workspaces/A34/andromda341/cartridges/andromda-spring"); 095 //printUsageAndExit(); 096 } 097 if (file == null) 098 { 099 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}