1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest; 28 29 import java.io.*; 30 import java.lang.reflect.InvocationTargetException; 31 import java.net.URL; 32 import java.net.URLClassLoader; 33 import java.nio.charset.StandardCharsets; 34 import java.text.MessageFormat; 35 import java.util.Properties; 36 import java.util.ResourceBundle; 37 38 import com.sun.interview.Interview; 39 import com.sun.interview.Question; 40 import com.sun.interview.WizPrint; 41 import com.sun.javatest.util.BackupPolicy; 42 import com.sun.javatest.util.I18NResourceBundle; 43 44 /** 45 * This class provides a utility for command-line editing of configuration (.jti) files. 46 * It is intended to be invoked from the command line, as in: <pre> 47 * java com.sun.javatest.EditJIT options... 48 * </pre> 49 * For details of the options, use the <code>-help</code> option. 50 */ 51 public class EditJTI 52 { 53 /** 54 * This exception is used to indicate a problem with the command line arguments. 55 */ 56 public static class BadArgs extends Exception 57 { 58 /** 59 * Create a BadArgs exception. 60 * @param i18n A resource bundle in which to find the detail message. 61 * @param s The key for the detail message. 62 */ 63 BadArgs(ResourceBundle i18n, String s) { 64 super(i18n.getString(s)); 65 } 66 67 /** 68 * Create a BadArgs exception. 69 * @param i18n A resource bundle in which to find the detail message. 70 * @param s The key for the detail message. 71 * @param o An argument to be formatted with the detail message by 72 * {@link java.text.MessageFormat#format} 73 */ BadArgs(ResourceBundle i18n, String s, Object o) { 74 super(MessageFormat.format(i18n.getString(s), new Object[] {o})); 75 } 76 77 78 /** 79 * Create a BadArgs exception. 80 * @param i18n A resource bundle in which to find the detail message. 81 * @param s The key for the detail message. 82 * @param o An array of arguments to be formatted with the detail message by 83 * {@link java.text.MessageFormat#format} 84 */ 85 BadArgs(ResourceBundle i18n, String s, Object[] o) { 86 super(MessageFormat.format(i18n.getString(s), o)); 87 } 88 } 89 90 /** 91 * This exception is used to report problems that arise when using this API. 92 */ 93 public static class Fault extends Exception 94 { 95 Fault(I18NResourceBundle i18n, String s) { 96 super(i18n.getString(s)); 97 } 98 99 Fault(I18NResourceBundle i18n, String s, Object o) { 100 super(i18n.getString(s, o)); 101 } 102 103 Fault(I18NResourceBundle i18n, String s, Object[] o) { 104 super(i18n.getString(s, o)); 105 } 106 } 107 108 /** 109 * Command line entry point. Run with <code>-help</code> to get 110 * brief command line help. Warning: this method uses System.exit 111 * and so does not return if called directly. 112 * @param args Comamnd line arguments. 113 */ 114 public static void main(String[] args) { 115 try { 116 EditJTI e = new EditJTI(); 117 boolean ok = e.run(args); 118 System.exit(ok ? 0 : 1); 119 } 120 catch (BadArgs e) { 121 System.err.println(e.getMessage()); 122 usage(System.err); 123 System.exit(2); 124 } 125 catch (Fault e) { 126 System.err.println(e.getMessage()); 127 System.exit(3); 128 } 129 } 130 131 /** 132 * Print out brief command line help. 133 * @param out the stream to which to write the command line help. 134 */ 135 public static void usage(PrintStream out) { 136 String prog = System.getProperty("program", "java " + EditJTI.class.getName()); 137 out.println(i18n.getString("editJTI.usage.title")); 138 out.print(" "); 139 out.print(prog); 140 out.println(i18n.getString("editJTI.usage.summary")); 141 142 out.println(i18n.getString("editJTI.usage.options")); 143 out.println(i18n.getString("editJTI.usage.help1")); 144 out.println(i18n.getString("editJTI.usage.help2")); 145 out.println(i18n.getString("editJTI.usage.help3")); 146 out.println(i18n.getString("editJTI.usage.classpath1")); 147 out.println(i18n.getString("editJTI.usage.classpath2")); 148 out.println(i18n.getString("editJTI.usage.log1")); 149 out.println(i18n.getString("editJTI.usage.log2")); 150 out.println(i18n.getString("editJTI.usage.outfile1")); 151 out.println(i18n.getString("editJTI.usage.outfile2")); 152 out.println(i18n.getString("editJTI.usage.path1")); 153 out.println(i18n.getString("editJTI.usage.path2")); 154 out.println(i18n.getString("editJTI.usage.preview1")); 155 out.println(i18n.getString("editJTI.usage.preview2")); 156 out.println(i18n.getString("editJTI.usage.ts1")); 157 out.println(i18n.getString("editJTI.usage.ts2")); 158 out.println(i18n.getString("editJTI.usage.verbose1")); 159 out.println(i18n.getString("editJTI.usage.verbose2")); 160 out.println(""); 161 out.println(i18n.getString("editJTI.usage.edit")); 162 out.println(i18n.getString("editJTI.usage.set")); 163 out.println(i18n.getString("editJTI.usage.search")); 164 out.println(""); 165 } 166 167 /** 168 * Run the utility, without exiting. Any messages are written to 169 * the standard output stream. 170 * @param args command line args 171 * @return true if the resulting configuration is valid (complete), 172 * and false otherwise. 173 * @throws EditJTI.BadArgs if there is an error analysing the args 174 * @throws EditJTI.Fault if there is an error executing the args 175 */ 176 public boolean run(String[] args) throws BadArgs, Fault { 177 PrintWriter out = new PrintWriter(System.out); 178 try { 179 return run(args, out); 180 } 181 finally { 182 out.flush(); 183 } 184 } 185 186 187 /** 188 * Run the utility, without exiting, writing any messages to a specified stream. 189 * @param args command line args 190 * @param out the stream to which to write any messages 191 * @return true if the resulting configuration is valid (complete), 192 * and false otherwise. 193 * @throws EditJTI.BadArgs if there is an error analysing the args 194 * @throws EditJTI.Fault if there is an error executing the args 195 */ 196 public boolean run(String[] args, PrintWriter out) throws BadArgs, Fault { 197 File inFile = null; 198 File outFile = null; 199 File logFile = null; 200 File classPath = null; 201 File testSuitePath = null; 202 File workDirPath = null; 203 String[] editCmds = null; 204 boolean helpFlag = false; 205 boolean previewFlag = false; 206 boolean showPathFlag = false; 207 boolean verboseFlag = false; 208 209 for (int i = 0; i < args.length; i++) { 210 if ((args[i].equals("-o") || args[i].equals("-out")) 211 && i + 1 < args.length) { 212 checkUnset(outFile, args[i]); 213 outFile = new File(args[++i]); 214 } 215 else if ((args[i].equals("-i") || args[i].equals("-in")) 216 && i + 1 < args.length) { 217 checkUnset(inFile, args[i]); 218 inFile = new File(args[++i]); 219 } 220 else if ((args[i].equals("-l") || args[i].equals("-log")) 221 && i + 1 < args.length) { 222 checkUnset(logFile, args[i]); 223 logFile = new File(args[++i]); 224 } 225 else if (args[i].equals("-n") || args[i].equals("-preview")) 226 previewFlag = true; 227 else if (args[i].equals("-p") || args[i].equals("-path")) 228 showPathFlag = true; 229 else if (args[i].equals("-v") || args[i].equals("-verbose") ) 230 verboseFlag = true; 231 else if ((args[i].equals("-cp") || args[i].equals("-classpath")) && i + 1 < args.length) { 232 checkUnset(classPath, args[i]); 233 classPath = new File(args[++i]); 234 } 235 else if ((args[i].equals("-ts") || args[i].equals("-testsuite")) && i + 1 < args.length) { 236 checkUnset(testSuitePath, args[i]); 237 testSuitePath = new File(args[++i]); 238 } 239 else if ((args[i].equals("-wd") || args[i].equals("-workdir")) && i + 1 < args.length) { 240 checkUnset(testSuitePath, args[i]); 241 workDirPath = new File(args[++i]); 242 } 243 else if (args[i].equals("-help") || args[i].equals("-usage") || args[i].equals("/?") ) 244 helpFlag = true; 245 else if (args[i].startsWith("-")) 246 throw new BadArgs(i18n, "editJTI.badOption", args[i]); 247 else if (i <= args.length - 1) { 248 if (inFile == null) { 249 editCmds = new String[args.length - 1 - i]; 250 System.arraycopy(args, i, editCmds, 0, editCmds.length); 251 inFile = new File(args[args.length - 1]); 252 } 253 else { 254 editCmds = new String[args.length - i]; 255 System.arraycopy(args, i, editCmds, 0, editCmds.length); 256 } 257 i = args.length - 1; 258 } 259 else 260 throw new BadArgs(i18n, "editJTI.badOption", args[i]); 261 } 262 263 if (args.length == 0 || helpFlag) { 264 usage(System.out); 265 if (inFile == null) 266 return true; 267 } 268 269 if (classPath != null && testSuitePath != null) 270 throw new BadArgs(i18n, "editJTI.cantHaveClassPathAndTestSuite"); 271 272 if (inFile == null) 273 throw new BadArgs(i18n, "editJTI.noInterview"); 274 275 // if (editCmds.length == 0 && outFile == null && logFile == null && !showPathFlag) 276 // throw new BadArgs(...no.actions....); 277 278 verbose = verboseFlag; 279 this.out = out; 280 281 try { 282 /* the following looks nice and simple, but breaks compatibility 283 with 3.1.4, because InterviewParameters.open will try and open 284 the wd in the .jti file if not given explicitly -- and previously, 285 this was not required/done. So, only use the simple code if wd 286 is set, and use the old 3.1.4 code if wd is not set. 287 */ 288 /* See comment above 289 if (workDirPath != null || testSuitePath != null) { 290 interview = InterviewParameters.open(testSuitePath, workDirPath, inFile); 291 } 292 */ 293 if (workDirPath != null) 294 interview = InterviewParameters.open(testSuitePath, workDirPath, inFile); 295 else if (testSuitePath != null) { 296 // only open the test suite, not the work dir 297 TestSuite ts; 298 try { 299 ts = TestSuite.open(testSuitePath); 300 } 301 catch (FileNotFoundException e) { 302 throw new Fault(i18n, "editJTI.cantFindTestSuite", testSuitePath); 303 } 304 catch (TestSuite.NotTestSuiteFault e) { 305 throw new Fault(i18n, "editJTI.notATestSuite", testSuitePath); 306 } 307 catch (TestSuite.Fault e) { 308 throw new Fault(i18n, "editJTI.cantOpenTestSuite", 309 new Object[] { testSuitePath, e }); 310 } 311 load(inFile, ts); 312 } 313 // End of patches for 3.1.4 compatibility 314 else if (classPath != null) { 315 URLClassLoader loader = new URLClassLoader(new URL[] { classPath.toURL() }); 316 load(inFile, loader); 317 } 318 else 319 load(inFile); 320 321 } 322 catch (Interview.Fault e) { 323 throw new Fault(i18n, "editJTI.cantOpenFile", 324 new Object[] { inFile.getPath(), e.getMessage() }); 325 } 326 catch (FileNotFoundException e) { 327 throw new Fault(i18n, "editJTI.cantFindFile", inFile.getPath()); 328 } 329 catch (IOException e) { 330 throw new Fault(i18n, "editJTI.cantOpenFile", 331 new Object[] { inFile.getPath(), e }); 332 } 333 catch (IllegalStateException e) { 334 // only occurs if keywords are being used in the config, and the 335 // test suite is not available. user needs to specify -wd or -ts 336 if (verbose) 337 e.printStackTrace(); 338 339 throw new Fault(i18n, "editJTI.badState", e.getMessage()); 340 } 341 342 if (NUM_BACKUPS > 0) 343 interview.setBackupPolicy(BackupPolicy.simpleBackups(NUM_BACKUPS)); 344 345 if (editCmds != null) 346 edit(editCmds); 347 348 349 if (showPathFlag) 350 showPath(); 351 352 try { 353 if (logFile != null) { 354 if (previewFlag) { 355 String msg = i18n.getString("editJTI.wouldWriteLog", logFile); 356 out.println(msg); 357 } 358 else 359 writeLog(logFile); 360 } 361 } 362 catch (IOException e) { 363 throw new Fault(i18n, "editJTI.cantWriteLog", 364 new Object[] { logFile.getPath(), e }); 365 } 366 367 368 try { 369 if (previewFlag) { 370 String msg; 371 if (interview.isEdited()) 372 msg = i18n.getString("editJTI.wouldSaveEdited", 373 (outFile != null ? outFile : inFile)); 374 else if (outFile != null) 375 msg = i18n.getString("editJTI.wouldSaveNotEdited", outFile); 376 else 377 msg = i18n.getString("editJTI.wouldNotSave"); 378 out.println(msg); 379 } 380 else { 381 if (outFile != null) 382 save(outFile); 383 else if (interview.isEdited()) 384 save(inFile); 385 } 386 } 387 catch (Interview.Fault e) { 388 throw new Fault(i18n, "editJTI.cantOpenFile", 389 new Object[] { 390 (outFile == null || outFile.getPath() == null ? 391 "??": outFile.getPath()), e }); 392 } 393 catch (IOException e) { 394 File f = (outFile == null ? interview.getFile() : outFile); 395 throw new Fault(i18n, "editJTI.cantSaveFile", 396 new Object[] { f.getPath(), e }); 397 } 398 399 return (interview.isFinishable()); 400 } 401 402 /** 403 * Load a configuration file to be edited. 404 * @param inFile the file to be loaded 405 * @throws IOException if there is a problem reading the file 406 * @throws Interview.Fault if there is a problem loading the interview data from the file 407 */ 408 public void load(File inFile) throws IOException, Interview.Fault { 409 // this opens the interview via the work directory and test suite; 410 // the test suite implicitly knows its classpath via the .jtt file 411 interview = InterviewParameters.open(inFile); 412 interview.setEdited(false); 413 } 414 415 /** 416 * Load a configuration file to be edited. 417 * @param inFile the file to be loaded 418 * @param ts the test suite for which the interview is to be loaded 419 * @throws IOException if there is a problem reading the file 420 * @throws Interview.Fault if there is a problem loading the interview data from the file 421 * @throws EditJTI.Fault if there is a problem creating the interview for the testsuite 422 */ 423 public void load(File inFile, TestSuite ts) 424 throws IOException, Interview.Fault, Fault 425 { 426 // this opens the interview via the work directory and test suite; 427 // the test suite implicitly knows its classpath via the .jtt file 428 try { 429 interview = ts.createInterview(); 430 } 431 catch (TestSuite.Fault e) { 432 throw new Fault(i18n, "editJTI.cantCreateInterviewForTestSuite", 433 new Object[] { ts.getPath(), e.getMessage() }); 434 } 435 interview.load(inFile); 436 interview.setEdited(false); 437 } 438 439 /** 440 * Load a configuration file to be edited, using a specified class loader 441 * to load the interview class. 442 * @param inFile the file to be loaded 443 * @param loader the class loader to be used to load the interview class 444 * @throws IOException if there is a problem reading the file 445 * @throws Interview.Fault if there is a problem loading the interview data from the file 446 * @throws EditJTI.Fault if there is a problem creating the interview for the testsuite 447 */ 448 public void load(File inFile, URLClassLoader loader) 449 throws IOException, Interview.Fault, Fault 450 { 451 InputStream in = new BufferedInputStream(new FileInputStream(inFile)); 452 Properties p = new Properties(); 453 p.load(in); 454 in.close(); 455 456 String interviewClassName = (String) (p.get("INTERVIEW")); 457 try { 458 Class<? extends InterviewParameters> interviewClass = 459 loader.loadClass(interviewClassName).asSubclass(InterviewParameters.class); 460 461 interview = interviewClass.getDeclaredConstructor().newInstance(); 462 } 463 catch (ClassCastException e) { 464 throw new Fault(i18n, "editJTI.invalidInterview", inFile); 465 } 466 catch (ClassNotFoundException e) { 467 throw new Fault(i18n, "editJTI.cantFindClass", 468 new Object[] { interviewClassName, inFile }); 469 } 470 catch (NoSuchMethodException | InstantiationException | InvocationTargetException e) { 471 throw new Fault(i18n, "editJTI.cantInstantiateClass", 472 new Object[] { interviewClassName, inFile }); 473 } 474 catch (IllegalAccessException e) { 475 throw new Fault(i18n, "editJTI.cantAccessClass", 476 new Object[] { interviewClassName, inFile }); 477 } 478 finally { 479 try { if (in != null) in.close(); } catch (IOException e) {} 480 } 481 482 interview.load(inFile); 483 interview.setEdited(false); 484 } 485 486 /** 487 * Save the edited configuration in a specified file. 488 * @param file The file in which to save the configuration 489 * @throws IOException if there is a problem while writing the file 490 * @throws Interview.Fault if there is a problem while saving the interview data 491 */ 492 public void save(File file) throws IOException, Interview.Fault { 493 interview.save(file); 494 } 495 496 /** 497 * Show the current question path for the configuration. 498 */ 499 public void showPath() { 500 Question[] path = interview.getPath(); 501 502 int indent = 0; 503 for (int i = 0; i < path.length; i++) 504 indent = Math.max(indent, path[i].getTag().length()); 505 indent = Math.min(indent, MAX_INDENT); 506 507 for (int i = 0; i < path.length; i++) { 508 Question q = path[i]; 509 String tag = q.getTag(); 510 String value = q.getStringValue(); 511 out.print(tag); 512 int l = tag.length(); 513 if (l > MAX_INDENT && value != null && value.length() > 0) { 514 out.println(); 515 l = 0; 516 } 517 for (int x = l; x < indent; x++) 518 out.print(' '); 519 out.print(' '); 520 out.println(value == null ? "" : value); 521 } 522 } 523 524 /** 525 * Write a log of the questions that determine the current configuration. 526 * @param logFile the file to which to write the log 527 * @throws IOException if there is a problem while writing the log file 528 */ 529 public void writeLog(File logFile) throws IOException { 530 WizPrint wp = new WizPrint(interview); 531 wp.setShowResponses(true); 532 wp.setShowResponseTypes(false); 533 wp.setShowTags(true); 534 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile), StandardCharsets.UTF_8)); 535 wp.write(out); 536 } 537 538 /** 539 * Apply a series of edits to the current configuration. 540 * @param cmds the editing commands to be applied 541 * @throws EditJTI.Fault if there is a problem while applying the edit commands. 542 * @see #edit(String) 543 */ 544 public void edit(String[] cmds) throws Fault { 545 for (int i = 0; i < cmds.length; i++) { 546 edit(cmds[i]); 547 } 548 } 549 550 /** 551 * Apply an edit to the current configuration. 552 * @param cmd the editing command to be applied 553 * Currently, two forms of command are supported: <dl> 554 * <dt><em>tag-name=value</em> 555 * <dd>Set the response to the question whose value is <em>tag-name</em> to <em>value</em> 556 * <dt><em>/search/replace/</em> 557 * <dd>For all questions on the current path, change instances of <em>search</em> to <em>replace</em> 558 * </dl> 559 * @throws EditJTI.Fault if there is a problem while applying the edit commands. 560 * @see #edit(String[]) 561 */ 562 public void edit(String cmd) throws Fault { 563 if (cmd == null || cmd.length() == 0) 564 return; 565 566 int eqIndex = cmd.indexOf('='); 567 if (Character.isJavaIdentifierStart(cmd.charAt(0)) && eqIndex > 0) 568 setValue(cmd.substring(0, eqIndex), cmd.substring(eqIndex + 1)); 569 else if (cmd.toLowerCase().startsWith("import:")) { 570 importFile(new File(cmd.substring("import:".length()))); 571 } 572 else { 573 int left = 0; 574 // could support a command letter in front? 575 char delim = cmd.charAt(left); 576 int center = cmd.indexOf(delim, left + 1); 577 if (center == -1) 578 throw new Fault(i18n, "editJTI.badCmd", cmd); 579 // could support trailing flags? 580 int right = cmd.length() - 1; 581 if (cmd.charAt(right) != delim) 582 throw new Fault(i18n, "editJTI.badCmd", cmd); 583 String searchText = cmd.substring(left + 1, center); 584 String replaceText = cmd.substring(center + 1, right); 585 if (searchText.length() == 0) 586 throw new Fault(i18n, "editJTI.badCmd", cmd); 587 setMatchingValues(searchText, replaceText); 588 } 589 } 590 591 private void importFile(File file) throws Fault { 592 593 InputStream in; 594 try { 595 in = new BufferedInputStream(new FileInputStream(file)); 596 } 597 catch (FileNotFoundException e) { 598 throw new Fault(i18n, "editJTI.cantFindImport", new Object[] { file, e }); 599 } 600 601 Properties p; 602 try { 603 p = new Properties(); 604 p.load(in); 605 in.close(); 606 } 607 catch (IOException e) { 608 throw new Fault(i18n, "editJTI.cantReadImport", 609 new Object[] { file, e }); 610 } 611 finally { 612 try { if (in != null) in.close(); } catch (IOException e) {} 613 } 614 615 // for each question on the path, see if there is a corresponding 616 // imported value 617 Question[] path = interview.getPath(); 618 for (int i = 0; i < path.length; i++) { 619 Question q = path[i]; 620 String v = p.getProperty(q.getTag()); 621 if (v != null) { 622 setValue(q, v); 623 path = interview.getPath(); // update path in case tail has changed 624 } 625 } 626 } 627 628 private void setMatchingValues(String searchText, String replaceText) throws Fault { 629 boolean found = false; 630 631 Question[] path = interview.getPath(); 632 for (int i = 0; i < path.length; i++) { 633 Question q = path[i]; 634 String currValue = q.getStringValue(); 635 if (currValue == null) 636 continue; 637 // currently hardwired: considerCase: false; word match: false 638 int pos = match(searchText, currValue, false, false); 639 if (pos >= 0) { 640 String newValue = currValue.substring(0, pos) 641 + replaceText 642 + currValue.substring(pos + searchText.length()); 643 setValue(q, newValue); 644 found = true; 645 path = interview.getPath(); // update path in case tail has changed 646 } 647 } 648 if (!found) 649 throw new Fault(i18n, "editJTI.cantFindMatch", searchText); 650 } 651 652 private void setValue(String tag, String value) throws Fault { 653 Question[] path = interview.getPath(); 654 for (int i = 0; i < path.length; i++) { 655 Question q = path[i]; 656 if (q.getTag().equals(tag)) { 657 setValue(q, value); 658 return; 659 } 660 } 661 throw new Fault(i18n, "editJTI.cantFindQuestion", tag); 662 } 663 664 private void setValue(Question q, String value) throws Fault { 665 try { 666 String oldValue = q.getStringValue(); 667 q.setValue(value); 668 if (verbose) 669 out.println(i18n.getString("editJTI.update", 670 new Object[] { q.getTag(), oldValue, q.getStringValue() })); 671 } 672 catch (Interview.Fault e) { 673 throw new Fault(i18n, "editJTI.cantSetValue", new Object[] { q.getTag(), e.getMessage() } ); 674 } 675 } 676 677 private static int match(String s1, String s2, boolean considerCase, boolean word) { 678 int s1len = s1.length(); 679 int s2len = s2.length(); 680 for (int i = 0; i <= s2len - s1len; i++) { 681 if (s1.regionMatches(!considerCase, 0, s2, i, s1len)) { 682 if (!word || (word && 683 ( (i == 0 || isBoundaryCh(s2.charAt(i-1))) 684 && (i+s1len == s2.length() || isBoundaryCh(s2.charAt(i+s1len))) ))) 685 return i; 686 } 687 } 688 return -1; 689 } 690 691 private static boolean isBoundaryCh(char c) { 692 return !(Character.isUnicodeIdentifierStart(c) 693 || Character.isUnicodeIdentifierPart(c)); 694 } 695 696 private static void checkUnset(Object item, String option) 697 throws BadArgs 698 { 699 if (item != null) 700 throw new BadArgs(i18n, "editJTI.dupOption",option); 701 } 702 703 private InterviewParameters interview; 704 private boolean verbose; 705 private PrintWriter out; 706 707 private static int MAX_INDENT = Integer.getInteger("EditJTI.maxIndent", 32).intValue(); 708 private static int NUM_BACKUPS = Integer.getInteger("EditJTI.numBackups", 2).intValue(); 709 710 private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(EditJTI.class); 711 }