001/** 002 * RenameWand 2.2 003 * Copyright 2007 Zach Scrivena 004 * 2007-12-09 005 * zachscrivena@gmail.com 006 * http://renamewand.sourceforge.net/ 007 * 008 * RenameWand is a simple command-line utility for renaming files or 009 * directories using an intuitive but powerful syntax. 010 * 011 * TERMS AND CONDITIONS: 012 * This program is free software: you can redistribute it and/or modify 013 * it under the terms of the GNU General Public License as published by 014 * the Free Software Foundation, either version 3 of the License, or 015 * (at your option) any later version. 016 * 017 * This program is distributed in the hope that it will be useful, 018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 020 * GNU General Public License for more details. 021 * 022 * You should have received a copy of the GNU General Public License 023 * along with this program. If not, see <http://www.gnu.org/licenses/>. 024 */ 025 026package ca.bc.webarts.tools.renamewand; 027 028import java.io.File; 029import java.text.SimpleDateFormat; 030import java.util.Date; 031import java.util.Locale; 032import java.util.Random; 033 034 035/** 036 * Represent a (candidate) matched file/directory. 037 */ 038class FileUnit 039{ 040 /** source file/directory (absolute and canonical abstract pathname) */ 041 File source = null; 042 043 /** parent directory identifier */ 044 int parentDirId = -1; 045 046 /** global file/directory count */ 047 int globalCount = -1; 048 049 /** local file/directory count */ 050 int localCount = -1; 051 052 /** register values */ 053 String[] registerValues = null; 054 055 /** target file/directory name */ 056 StringBuilder targetFilename = null; 057 058 /** target file/directory (absolute and canonical abstract pathname) */ 059 File target = null; 060 061 062 /** 063 * Evaluate the specified macro, by returning the value of the specified 064 * macro or assigned register corresponding to this file/directory. 065 * 066 * @param macro 067 * Macro or register name 068 * @return 069 * String representation of the macro or assigned register value; 070 * null if macro or register is invalid or unassigned 071 */ 072 String evaluateMacro( 073 final String macro) 074 { 075 /************************ 076 * (1) FILE NAME MACROS * 077 ************************/ 078 079 /* Example: "C:\Work\2007\Jan\Report.txt", with "C:\Work" as current directory */ 080 081 /* Filename ("Report.txt") */ 082 if ("FN".equals(macro)) 083 { 084 String name = this.source.getName(); 085 086 /* trim off trailing separator */ 087 while (name.endsWith(File.separator)) 088 name = name.substring(0, name.length() - File.separator.length()); 089 090 return name; 091 } 092 093 /* File extension ("txt") */ 094 if ("FN.ext".equals(macro)) 095 { 096 String name = this.source.getName(); 097 098 /* trim off trailing separator */ 099 while (name.endsWith(File.separator)) 100 name = name.substring(0, name.length() - File.separator.length()); 101 102 final int i = name.lastIndexOf("."); 103 104 if (i < 0) 105 { 106 return ""; 107 } 108 else 109 { 110 return name.substring(i + 1); 111 } 112 } 113 114 /* Base filename without extension ("Report") */ 115 if ("FN.name".equals(macro)) 116 { 117 String name = this.source.getName(); 118 119 /* trim off trailing separator */ 120 while (name.endsWith(File.separator)) 121 name = name.substring(0, name.length() - File.separator.length()); 122 123 final int i = name.lastIndexOf("."); 124 125 if (i < 0) 126 { 127 return name; 128 } 129 else 130 { 131 return name.substring(0, i); 132 } 133 } 134 135 /* Relative pathname ("2007\Jan\Report.txt") */ 136 if ("FN.path".equals(macro)) 137 { 138 String path = this.source.getPath(); 139 140 if (path.startsWith(RenameWand.currentDirectoryFullPathname)) 141 path = path.substring(RenameWand.currentDirectoryFullPathnameLength); 142 143 /* trim off trailing separator */ 144 while (path.endsWith(File.separator)) 145 path = path.substring(0, path.length() - File.separator.length()); 146 147 return path; 148 } 149 150 /* Parent directory ("Jan") */ 151 if ("FN.parent".equals(macro)) 152 { 153 final File parent = this.source.getParentFile(); 154 155 if ((parent == null) || (parent.equals(RenameWand.currentDirectory))) 156 { 157 return ""; 158 } 159 else 160 { 161 String name = parent.getName(); 162 163 /* trim off trailing separator */ 164 while (name.endsWith(File.separator)) 165 name = name.substring(0, name.length() - File.separator.length()); 166 167 return name; 168 } 169 } 170 171 /* Relative pathname of parent directory ("2007\Jan") */ 172 if ("FN.parentpath".equals(macro)) 173 { 174 final File parent = this.source.getParentFile(); 175 176 if ((parent == null) || (parent.equals(RenameWand.currentDirectory))) 177 { 178 return ""; 179 } 180 else 181 { 182 String path = parent.getPath(); 183 184 if (path.startsWith(RenameWand.currentDirectoryFullPathname)) 185 path = path.substring(RenameWand.currentDirectoryFullPathnameLength); 186 187 /* trim off trailing separator */ 188 while (path.endsWith(File.separator)) 189 path = path.substring(0, path.length() - File.separator.length()); 190 191 return path; 192 } 193 } 194 195 196 /************************ 197 * (2) FILE SIZE MACROS * 198 ************************/ 199 200 /* all values are cast as integers */ 201 202 /* File size in bytes */ 203 if ("FS".equals(macro)) 204 return this.source.length() + ""; 205 206 /* File size in kilobytes (2^10 bytes) */ 207 if ("FS.kB".equals(macro)) 208 return (this.source.length() / 1024L) + ""; 209 210 /* File size in megabytes (2^20 bytes) */ 211 if ("FS.MB".equals(macro)) 212 return (this.source.length() / 1048576L) + ""; 213 214 /* File size in gigabytes (2^30 bytes) */ 215 if ("FS.GB".equals(macro)) 216 return (this.source.length() / 1073741824L) + ""; 217 218 /* File size in terabytes (2^40 bytes) */ 219 if ("FS.TB".equals(macro)) 220 return (this.source.length() / 1099511627776L) + ""; 221 222 223 /************************************** 224 * (3) FILE LAST-MODIFIED TIME MACROS * 225 **************************************/ 226 227 /* Number of milliseconds since the epoch (January 1, 1970 00:00:00 GMT, Gregorian) */ 228 if ("FT".equals(macro)) 229 return this.source.lastModified() + ""; 230 231 /* Date in the form yyyyMMdd */ 232 if ("FT.date".equals(macro)) 233 return (new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH)).format(new Date(this.source.lastModified())); 234 235 /* Time in the form HHmmss */ 236 if ("FT.time".equals(macro)) 237 return (new SimpleDateFormat("HHmmss", Locale.ENGLISH)).format(new Date(this.source.lastModified())); 238 239 /* am/pm in lower case */ 240 if ("FT.ap".equals(macro)) 241 return (new SimpleDateFormat("a", Locale.ENGLISH)).format(new Date(this.source.lastModified())).toLowerCase(Locale.ENGLISH); 242 243 /* AM/PM in upper case */ 244 if ("FT.AP".equals(macro)) 245 return (new SimpleDateFormat("a", Locale.ENGLISH)).format(new Date(this.source.lastModified())).toUpperCase(Locale.ENGLISH); 246 247 /* Date and time pattern letters from Java */ 248 if (macro.startsWith("FT.")) 249 { 250 final String macroName = macro.substring(macro.indexOf(".") + 1); 251 252 if (macroName.length() > 0) 253 { 254 boolean validChars = true; 255 256 for (int i = 0; i < macroName.length(); i++) 257 { 258 if (!"GyMwWDdFEaHkKhmsSzZ".contains(macroName.charAt(i) + "")) 259 { 260 validChars = false; 261 break; 262 } 263 } 264 265 if (validChars) 266 return (new SimpleDateFormat(macroName, Locale.ENGLISH)).format(new Date(this.source.lastModified())); 267 } 268 } 269 270 271 /*************************** 272 * (4) CURRENT TIME MACROS * 273 ***************************/ 274 275 /* Number of milliseconds since the epoch (January 1, 1970 00:00:00 GMT, Gregorian) */ 276 if ("CT".equals(macro)) 277 return (new Date()).getTime() + ""; 278 279 /* Date in the form yyyyMMdd */ 280 if ("CT.date".equals(macro)) 281 return (new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH)).format(new Date()); 282 283 /* Time in the form HHmmss */ 284 if ("CT.time".equals(macro)) 285 return (new SimpleDateFormat("HHmmss", Locale.ENGLISH)).format(new Date()); 286 287 /* am/pm in lower case */ 288 if ("CT.ap".equals(macro)) 289 return (new SimpleDateFormat("a", Locale.ENGLISH)).format(new Date()).toLowerCase(Locale.ENGLISH); 290 291 /* AM/PM in upper case */ 292 if ("CT.AP".equals(macro)) 293 return (new SimpleDateFormat("a", Locale.ENGLISH)).format(new Date()).toUpperCase(Locale.ENGLISH); 294 295 /* Date and time pattern letters from Java */ 296 if (macro.startsWith("CT.")) 297 { 298 final String macroName = macro.substring(macro.indexOf(".") + 1); 299 300 if (macroName.length() > 0) 301 { 302 boolean validChars = true; 303 304 for (int i = 0; i < macroName.length(); i++) 305 { 306 if (!"GyMwWDdFEaHkKhmsSzZ".contains(macroName.charAt(i) + "")) 307 { 308 validChars = false; 309 break; 310 } 311 } 312 313 if (validChars) 314 return (new SimpleDateFormat(macroName, Locale.ENGLISH)).format(new Date()); 315 } 316 } 317 318 319 /******************************************* 320 * (5) SYSTEM ENVIRONMENT VARIABLES MACROS * 321 *******************************************/ 322 323 if (macro.startsWith("ENV.")) 324 { 325 final String macroName = macro.substring(macro.indexOf(".") + 1); 326 327 if (macroName.length() > 0) 328 { 329 final String value = System.getenv(macroName); 330 331 if (value != null) 332 return value; 333 } 334 } 335 336 337 /******************************** 338 * (6) SYSTEM PROPERTIES MACROS * 339 ********************************/ 340 341 if (macro.startsWith("SYS.")) 342 { 343 final String macroName = macro.substring(macro.indexOf(".") + 1); 344 345 if (macroName.length() > 0) 346 { 347 final String value = System.getProperty(macroName); 348 349 if (value != null) 350 return value; 351 } 352 } 353 354 355 /**************************** 356 * (7) MISCELLANEOUS MACROS * 357 ****************************/ 358 359 /* Full pathname of the current directory (e.g. "C:\Work") */ 360 if ("RW.cd".equals(macro)) 361 { 362 String path = RenameWand.currentDirectoryFullPathname; 363 364 /* trim off trailing separator */ 365 while (path.endsWith(File.separator)) 366 path = path.substring(0, path.length() - File.separator.length()); 367 368 return path; 369 } 370 371 /* Number of local matched files (i.e. in the file's subdirectory) */ 372 if ("RW.N".equals(macro) && (this.localCount > 0)) 373 return this.localCount + ""; 374 375 /* Number of global matched files (i.e. in all subdirectories) */ 376 if ("RW.NN".equals(macro) && (this.globalCount > 0)) 377 return this.globalCount + ""; 378 379 /* Generate a string of 10 random digits */ 380 if ("RW.random".equals(macro)) 381 { 382 /* return a string of 10 random digits (0-9) */ 383 final Random rng = new Random(); 384 final StringBuilder randomString = new StringBuilder(); 385 386 /* append a digit */ 387 while (randomString.length() < 10) 388 randomString.append(rng.nextInt(10)); 389 390 return randomString.substring(0, 10); 391 } 392 393 394 /*********************************************** 395 * (8) MACRO/REGISTER MODIFIERS AND ATTRIBUTES * 396 ***********************************************/ 397 398 /* Example: "hello WORLD" */ 399 400 /* Length of the string (11) */ 401 if (macro.endsWith(".len")) 402 { 403 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 404 final String parentMacroValue = this.evaluateMacro(parentMacro); 405 406 if (parentMacroValue != null) 407 return parentMacroValue.length() + ""; 408 } 409 410 /* Convert to upper case ("HELLO WORLD") */ 411 if (macro.endsWith(".upper")) 412 { 413 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 414 final String parentMacroValue = this.evaluateMacro(parentMacro); 415 416 if (parentMacroValue != null) 417 return parentMacroValue.toUpperCase(); 418 } 419 420 /* Convert to lower case ("hello world") */ 421 if (macro.endsWith(".lower")) 422 { 423 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 424 final String parentMacroValue = this.evaluateMacro(parentMacro); 425 426 if (parentMacroValue != null) 427 return parentMacroValue.toLowerCase(); 428 } 429 430 /* Capitalize only the first character ("Hello world") */ 431 if (macro.endsWith(".capitalize")) 432 { 433 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 434 final String parentMacroValue = this.evaluateMacro(parentMacro); 435 436 if (parentMacroValue != null) 437 { 438 if (parentMacroValue.isEmpty()) 439 return parentMacroValue; 440 441 return Character.toUpperCase(parentMacroValue.charAt(0)) + 442 parentMacroValue.substring(1).toLowerCase(); 443 } 444 } 445 446 /* Convert to title case ("Hello World") */ 447 if (macro.endsWith(".title")) 448 { 449 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 450 final String parentMacroValue = this.evaluateMacro(parentMacro); 451 452 if (parentMacroValue != null) 453 return StringManipulator.toTitleCase(parentMacroValue); 454 } 455 456 /* Convert to camelCase ("helloWorld") */ 457 if (macro.endsWith(".camel")) 458 { 459 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 460 final String parentMacroValue = this.evaluateMacro(parentMacro); 461 462 if (parentMacroValue != null) 463 { 464 final String value = StringManipulator.deleteWhitespace(StringManipulator.toTitleCase(parentMacroValue)); 465 466 if (value.isEmpty()) 467 return value; 468 469 return Character.toLowerCase(value.charAt(0)) + value.substring(1); 470 } 471 } 472 473 /* Convert to PascalCase ("HelloWorld") */ 474 if (macro.endsWith(".pascal")) 475 { 476 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 477 final String parentMacroValue = this.evaluateMacro(parentMacro); 478 479 if (parentMacroValue != null) 480 return StringManipulator.deleteWhitespace(StringManipulator.toTitleCase(parentMacroValue)); 481 } 482 483 /* Swap the case ("HELLO world") */ 484 if (macro.endsWith(".swapcase")) 485 { 486 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 487 final String parentMacroValue = this.evaluateMacro(parentMacro); 488 489 if (parentMacroValue != null) 490 return StringManipulator.swapCase(parentMacroValue); 491 } 492 493 /* Abbreviate to initials ("h W") */ 494 if (macro.endsWith(".abbrev")) 495 { 496 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 497 final String parentMacroValue = this.evaluateMacro(parentMacro); 498 499 if (parentMacroValue != null) 500 return StringManipulator.abbreviate(parentMacroValue); 501 } 502 503 /* Reverse the string ("DLROW olleh") */ 504 if (macro.endsWith(".reverse")) 505 { 506 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 507 final String parentMacroValue = this.evaluateMacro(parentMacro); 508 509 if (parentMacroValue != null) 510 return StringManipulator.reverse(parentMacroValue); 511 } 512 513 /* Trim away whitespace on the left and right */ 514 if (macro.endsWith(".trim")) 515 { 516 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 517 final String parentMacroValue = this.evaluateMacro(parentMacro); 518 519 if (parentMacroValue != null) 520 return parentMacroValue.trim(); 521 } 522 523 /* Trim away whitespace on the left */ 524 if (macro.endsWith(".ltrim")) 525 { 526 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 527 final String parentMacroValue = this.evaluateMacro(parentMacro); 528 529 if (parentMacroValue != null) 530 return StringManipulator.leftTrim(parentMacroValue); 531 } 532 533 /* Trim away whitespace on the right */ 534 if (macro.endsWith(".rtrim")) 535 { 536 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 537 final String parentMacroValue = this.evaluateMacro(parentMacro); 538 539 if (parentMacroValue != null) 540 return StringManipulator.rightTrim(parentMacroValue); 541 } 542 543 /* Delete whitespace in the string */ 544 if (macro.endsWith(".delspace")) 545 { 546 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 547 final String parentMacroValue = this.evaluateMacro(parentMacro); 548 549 if (parentMacroValue != null) 550 return StringManipulator.deleteWhitespace(parentMacroValue); 551 } 552 553 /* Delete extra whitespace by replacing contiguous whitespace with a single space */ 554 if (macro.endsWith(".delextraspace")) 555 { 556 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 557 final String parentMacroValue = this.evaluateMacro(parentMacro); 558 559 if (parentMacroValue != null) 560 return StringManipulator.deleteExtraWhitespace(parentMacroValue); 561 } 562 563 /* Delete punctuation marks in the string */ 564 if (macro.endsWith(".delpunctuation")) 565 { 566 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 567 final String parentMacroValue = this.evaluateMacro(parentMacro); 568 569 if (parentMacroValue != null) 570 return StringManipulator.deletePunctuation(parentMacroValue); 571 } 572 573 /* Space out words by inserting a space between connected words */ 574 if (macro.endsWith(".spaceout")) 575 { 576 final String parentMacro = macro.substring(0, macro.lastIndexOf(".")); 577 final String parentMacroValue = this.evaluateMacro(parentMacro); 578 579 if (parentMacroValue != null) 580 return StringManipulator.spaceOutWords(parentMacroValue); 581 } 582 583 584 /**************************** 585 * (9) REGISTERS (ASSIGNED) * 586 ****************************/ 587 588 /* MUST BE THIRD-LAST BLOCK */ 589 590 if ((this.registerValues != null) && 591 RenameWand.registerNames.containsKey(macro)) 592 { 593 return this.registerValues[RenameWand.registerNames.get(macro)]; 594 } 595 596 597 /*************************************************** 598 * (10) SHORTCUTS FOR SINGLE-LETTER REGISTER NAMES * 599 ***************************************************/ 600 601 /* MUST BE SECOND-LAST BLOCK */ 602 603 /* Example: a ---> aa AA Aa aA 604 * Rule: If name is not a register, and is two chars long, 605 * and first char == second char (ignore case), and exclusively 606 * either small or big letter is a register name, then return 607 * .lower, .upper, .title, .swapcase 608 */ 609 if ((this.registerValues != null) && 610 (macro.length() == 2)) 611 { 612 final String c1 = macro.charAt(0) + ""; 613 final String c2 = macro.charAt(1) + ""; 614 615 if (c1.equalsIgnoreCase(c2)) 616 { 617 final String upper = c1.toUpperCase(Locale.ENGLISH); 618 final String lower = c1.toLowerCase(Locale.ENGLISH); 619 final boolean upperIsRegister = RenameWand.registerNames.containsKey(upper); 620 final boolean lowerIsRegister = RenameWand.registerNames.containsKey(lower); 621 String parentMacro = null; 622 623 if (upperIsRegister && !lowerIsRegister) 624 { 625 parentMacro = upper; 626 } 627 else if (lowerIsRegister && !upperIsRegister) 628 { 629 parentMacro = lower; 630 } 631 632 if (parentMacro != null) 633 { 634 if (macro.equals(lower + lower)) 635 return this.evaluateMacro(parentMacro + ".lower"); 636 637 if (macro.equals(upper + upper)) 638 return this.evaluateMacro(parentMacro + ".upper"); 639 640 if (macro.equals(upper + lower)) 641 return this.evaluateMacro(parentMacro + ".title"); 642 643 if (macro.equals(lower + upper)) 644 return this.evaluateMacro(parentMacro + ".swapcase"); 645 } 646 } 647 } 648 649 650 /*************************** 651 * (11) UNIDENTIFIED MACRO * 652 ***************************/ 653 654 /* MUST BE LAST BLOCK */ 655 656 return null; // this should be the only "return null" in this method 657 } 658}