< prev index next >

src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinExeBundler.java

Print this page




  25 
  26 package jdk.jpackage.internal;
  27 
  28 import java.io.*;
  29 import java.nio.charset.Charset;
  30 import java.nio.file.Files;
  31 import java.text.MessageFormat;
  32 import java.util.*;
  33 import java.util.regex.Matcher;
  34 import java.util.regex.Pattern;
  35 
  36 import static jdk.jpackage.internal.WindowsBundlerParam.*;
  37 
  38 public class WinExeBundler extends AbstractBundler {
  39 
  40     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  41             "jdk.jpackage.internal.resources.WinResources");
  42 
  43     public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
  44             new WindowsBundlerParam<>(
  45             getString("param.exe-bundler.name"),
  46             getString("param.exe-bundler.description"),
  47             "win.app.bundler",
  48             WinAppBundler.class,
  49             params -> new WinAppBundler(),
  50             null);
  51 
  52     public static final BundlerParamInfo<File> EXE_IMAGE_DIR =
  53             new WindowsBundlerParam<>(
  54             getString("param.image-dir.name"),
  55             getString("param.image-dir.description"),
  56             "win.exe.imageDir",
  57             File.class,
  58             params -> {
  59                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
  60                 if (!imagesRoot.exists()) imagesRoot.mkdirs();
  61                 return new File(imagesRoot, "win-exe.image");
  62             },
  63             (s, p) -> null);
  64 
  65     public static final BundlerParamInfo<File> WIN_APP_IMAGE =
  66             new WindowsBundlerParam<>(
  67             getString("param.app-dir.name"),
  68             getString("param.app-dir.description"),
  69             "win.app.image",
  70             File.class,
  71             null,
  72             (s, p) -> null);
  73 
  74     public static final BundlerParamInfo<UUID> UPGRADE_UUID =
  75             new WindowsBundlerParam<>(
  76             I18N.getString("param.upgrade-uuid.name"),
  77             I18N.getString("param.upgrade-uuid.description"),
  78             Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
  79             UUID.class,
  80             params -> UUID.randomUUID(),
  81             (s, p) -> UUID.fromString(s));
  82 
  83     public static final StandardBundlerParam<Boolean> EXE_SYSTEM_WIDE  =
  84             new StandardBundlerParam<>(
  85             getString("param.system-wide.name"),
  86             getString("param.system-wide.description"),
  87             Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
  88             Boolean.class,
  89             params -> true, // default to system wide
  90             (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
  91                     : Boolean.valueOf(s)
  92             );
  93     public static final StandardBundlerParam<String> PRODUCT_VERSION =
  94             new StandardBundlerParam<>(
  95                     getString("param.product-version.name"),
  96                     getString("param.product-version.description"),
  97                     "win.msi.productVersion",
  98                     String.class,
  99                     VERSION::fetchFrom,
 100                     (s, p) -> s
 101             );
 102 
 103     public static final StandardBundlerParam<Boolean> MENU_HINT =
 104         new WindowsBundlerParam<>(
 105                 getString("param.menu-shortcut-hint.name"),
 106                 getString("param.menu-shortcut-hint.description"),
 107                 Arguments.CLIOptions.WIN_MENU_HINT.getId(),
 108                 Boolean.class,
 109                 params -> false,
 110                 (s, p) -> (s == null ||
 111                         "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
 112         );
 113 
 114     public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
 115         new WindowsBundlerParam<>(
 116                 getString("param.desktop-shortcut-hint.name"),
 117                 getString("param.desktop-shortcut-hint.description"),
 118                 Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
 119                 Boolean.class,
 120                 params -> false,
 121                 (s, p) -> (s == null ||
 122                        "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
 123         );
 124 
 125     private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
 126     private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss";
 127     private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
 128 
 129     public static final BundlerParamInfo<String>
 130             TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(
 131             getString("param.iscc-path.name"),
 132             getString("param.iscc-path.description"),
 133             "win.exe.iscc.exe",
 134             String.class,
 135             params -> {
 136                 for (String dirString : (System.getenv("PATH")
 137                         + ";C:\\Program Files (x86)\\Inno Setup 5;"
 138                         + "C:\\Program Files\\Inno Setup 5").split(";")) {
 139                     File f = new File(dirString.replace("\"", ""),
 140                             TOOL_INNO_SETUP_COMPILER);
 141                     if (f.isFile()) {
 142                         return f.toString();
 143                     }
 144                 }
 145                 return null;
 146             },
 147             null);
 148 
 149     @Override
 150     public String getName() {
 151         return getString("exe.bundler.name");
 152     }


 357                 }
 358             }
 359         }
 360 
 361         return true;
 362     }
 363 
 364     public File bundle(Map<String, ? super Object> p, File outdir)
 365             throws PackagerException {
 366         if (!outdir.isDirectory() && !outdir.mkdirs()) {
 367             throw new PackagerException("error.cannot-create-output-dir",
 368                     outdir.getAbsolutePath());
 369         }
 370         if (!outdir.canWrite()) {
 371             throw new PackagerException("error.cannot-write-to-output-dir",
 372                     outdir.getAbsolutePath());
 373         }
 374 
 375         String tempDirectory = WindowsDefender.getUserTempDirectory();
 376         if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
 377             tempDirectory = BUILD_ROOT.fetchFrom(p).getAbsolutePath();
 378         }
 379         if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
 380                 tempDirectory)) {
 381             Log.error(MessageFormat.format(
 382                     getString("message.potential.windows.defender.issue"),
 383                     tempDirectory));
 384         }
 385 
 386         // validate we have valid tools before continuing
 387         String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
 388         if (iscc == null || !new File(iscc).isFile()) {
 389             Log.verbose(getString("error.iscc-not-found"));
 390             Log.verbose(MessageFormat.format(
 391                     getString("message.iscc-file-string"), iscc));
 392             throw new PackagerException("error.iscc-not-found");
 393         }
 394 
 395         File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
 396         try {
 397             imageDir.mkdirs();


 522         data.put("ARCHITECTURE_BIT_MODE", "x64");
 523 
 524         validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p);
 525 
 526         validateValueAndPut(data, "APPLICATION_DESCRIPTION",
 527                 DESCRIPTION, p);
 528 
 529         data.put("APPLICATION_SERVICE", "returnFalse");
 530         data.put("APPLICATION_NOT_SERVICE", "returnFalse");
 531         data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse");
 532         data.put("START_ON_INSTALL", "");
 533         data.put("STOP_ON_UNINSTALL", "");
 534         data.put("RUN_AT_STARTUP", "");
 535 
 536         String imagePathString =
 537                 WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString();
 538         data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString));
 539         Log.verbose("setting APPLICATION_IMAGE to " +
 540                 innosetupEscape(imagePathString) + " for InnoSetup");
 541 
 542         StringBuilder secondaryLaunchersCfg = new StringBuilder();
 543         for (Map<String, ? super Object>
 544                 launcher : SECONDARY_LAUNCHERS.fetchFrom(p)) {
 545             String application_name = APP_NAME.fetchFrom(launcher);
 546             if (MENU_HINT.fetchFrom(launcher)) {
 547                 // Name: "{group}\APPLICATION_NAME";
 548                 // Filename: "{app}\APPLICATION_NAME.exe";
 549                 // IconFilename: "{app}\APPLICATION_NAME.ico"
 550                 secondaryLaunchersCfg.append("Name: \"{group}\\");
 551                 secondaryLaunchersCfg.append(application_name);
 552                 secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
 553                 secondaryLaunchersCfg.append(application_name);
 554                 secondaryLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
 555                 secondaryLaunchersCfg.append(application_name);
 556                 secondaryLaunchersCfg.append(".ico\"\r\n");
 557             }
 558             if (SHORTCUT_HINT.fetchFrom(launcher)) {
 559                 // Name: "{commondesktop}\APPLICATION_NAME";
 560                 // Filename: "{app}\APPLICATION_NAME.exe";
 561                 // IconFilename: "{app}\APPLICATION_NAME.ico"
 562                 secondaryLaunchersCfg.append("Name: \"{commondesktop}\\");
 563                 secondaryLaunchersCfg.append(application_name);
 564                 secondaryLaunchersCfg.append("\"; Filename: \"{app}\\");
 565                 secondaryLaunchersCfg.append(application_name);
 566                 secondaryLaunchersCfg.append(".exe\";  IconFilename: \"{app}\\");
 567                 secondaryLaunchersCfg.append(application_name);
 568                 secondaryLaunchersCfg.append(".ico\"\r\n");
 569             }
 570         }
 571         data.put("SECONDARY_LAUNCHERS", secondaryLaunchersCfg.toString());
 572 
 573         StringBuilder registryEntries = new StringBuilder();
 574         String regName = APP_REGISTRY_NAME.fetchFrom(p);
 575         List<Map<String, ? super Object>> fetchFrom =
 576                 FILE_ASSOCIATIONS.fetchFrom(p);
 577         for (int i = 0; i < fetchFrom.size(); i++) {
 578             Map<String, ? super Object> fileAssociation = fetchFrom.get(i);
 579             String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
 580             File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO
 581 
 582             List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
 583             String entryName = regName + "File";
 584             if (i > 0) {
 585                 entryName += "." + i;
 586             }
 587 
 588             if (extensions == null) {
 589                 Log.verbose(getString(
 590                         "message.creating-association-with-null-extension"));
 591             } else {


 706                         .append(APP_NAME.fetchFrom(p))
 707                         .append("\"\" \"\"%1\"\"\"\r\n");
 708             } else {
 709                 registryEntries.append(
 710                         "Root: HKCU; Subkey: \"Software\\Classes\\")
 711                         .append(entryName)
 712                         .append("\\shell\\open\\command\"; ValueType: " +
 713                         "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
 714                         .append(APP_NAME.fetchFrom(p))
 715                         .append("\"\" \"\"%1\"\"\"\r\n");
 716             }
 717         }
 718         if (registryEntries.length() > 0) {
 719             data.put("FILE_ASSOCIATIONS",
 720                     "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" +
 721                     registryEntries.toString());
 722         } else {
 723             data.put("FILE_ASSOCIATIONS", "");
 724         }
 725 
 726         // TODO - alternate template for JRE installer
 727         String iss = RUNTIME_INSTALLER.fetchFrom(p) ?
 728                 DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE;
 729 
 730         Writer w = new BufferedWriter(new FileWriter(
 731                 getConfig_ExeProjectFile(p)));
 732 
 733         String content = preprocessTextResource(
 734                 getConfig_ExeProjectFile(p).getName(),
 735                 getString("resource.inno-setup-project-file"),
 736                 iss, data, VERBOSE.fetchFrom(p),
 737                 RESOURCE_DIR.fetchFrom(p));
 738         w.write(content);
 739         w.close();
 740         return true;
 741     }
 742 
 743     private final static String removeQuotes(String s) {
 744         if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) {
 745             // special case for '"XXX"' return 'XXX' not '-XXX-'
 746             // note '"' and '""' are excluded from this special case
 747             s = s.substring(1, s.length() - 1);




  25 
  26 package jdk.jpackage.internal;
  27 
  28 import java.io.*;
  29 import java.nio.charset.Charset;
  30 import java.nio.file.Files;
  31 import java.text.MessageFormat;
  32 import java.util.*;
  33 import java.util.regex.Matcher;
  34 import java.util.regex.Pattern;
  35 
  36 import static jdk.jpackage.internal.WindowsBundlerParam.*;
  37 
  38 public class WinExeBundler extends AbstractBundler {
  39 
  40     private static final ResourceBundle I18N = ResourceBundle.getBundle(
  41             "jdk.jpackage.internal.resources.WinResources");
  42 
  43     public static final BundlerParamInfo<WinAppBundler> APP_BUNDLER =
  44             new WindowsBundlerParam<>(


  45             "win.app.bundler",
  46             WinAppBundler.class,
  47             params -> new WinAppBundler(),
  48             null);
  49 
  50     public static final BundlerParamInfo<File> EXE_IMAGE_DIR =
  51             new WindowsBundlerParam<>(


  52             "win.exe.imageDir",
  53             File.class,
  54             params -> {
  55                 File imagesRoot = IMAGES_ROOT.fetchFrom(params);
  56                 if (!imagesRoot.exists()) imagesRoot.mkdirs();
  57                 return new File(imagesRoot, "win-exe.image");
  58             },
  59             (s, p) -> null);
  60 
  61     public static final BundlerParamInfo<File> WIN_APP_IMAGE =
  62             new WindowsBundlerParam<>(


  63             "win.app.image",
  64             File.class,
  65             null,
  66             (s, p) -> null);
  67 
  68     public static final BundlerParamInfo<UUID> UPGRADE_UUID =
  69             new WindowsBundlerParam<>(


  70             Arguments.CLIOptions.WIN_UPGRADE_UUID.getId(),
  71             UUID.class,
  72             params -> UUID.randomUUID(),
  73             (s, p) -> UUID.fromString(s));
  74 
  75     public static final StandardBundlerParam<Boolean> EXE_SYSTEM_WIDE  =
  76             new StandardBundlerParam<>(


  77             Arguments.CLIOptions.WIN_PER_USER_INSTALLATION.getId(),
  78             Boolean.class,
  79             params -> true, // default to system wide
  80             (s, p) -> (s == null || "null".equalsIgnoreCase(s))? null
  81                     : Boolean.valueOf(s)
  82             );
  83     public static final StandardBundlerParam<String> PRODUCT_VERSION =
  84             new StandardBundlerParam<>(


  85                     "win.msi.productVersion",
  86                     String.class,
  87                     VERSION::fetchFrom,
  88                     (s, p) -> s
  89             );
  90 
  91     public static final StandardBundlerParam<Boolean> MENU_HINT =
  92         new WindowsBundlerParam<>(


  93                 Arguments.CLIOptions.WIN_MENU_HINT.getId(),
  94                 Boolean.class,
  95                 params -> false,
  96                 (s, p) -> (s == null ||
  97                         "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
  98         );
  99 
 100     public static final StandardBundlerParam<Boolean> SHORTCUT_HINT =
 101         new WindowsBundlerParam<>(


 102                 Arguments.CLIOptions.WIN_SHORTCUT_HINT.getId(),
 103                 Boolean.class,
 104                 params -> false,
 105                 (s, p) -> (s == null ||
 106                        "null".equalsIgnoreCase(s))? false : Boolean.valueOf(s)
 107         );
 108 
 109     private final static String DEFAULT_EXE_PROJECT_TEMPLATE = "template.iss";
 110     private final static String DEFAULT_JRE_EXE_TEMPLATE = "template.jre.iss";
 111     private static final String TOOL_INNO_SETUP_COMPILER = "iscc.exe";
 112 
 113     public static final BundlerParamInfo<String>
 114             TOOL_INNO_SETUP_COMPILER_EXECUTABLE = new WindowsBundlerParam<>(


 115             "win.exe.iscc.exe",
 116             String.class,
 117             params -> {
 118                 for (String dirString : (System.getenv("PATH")
 119                         + ";C:\\Program Files (x86)\\Inno Setup 5;"
 120                         + "C:\\Program Files\\Inno Setup 5").split(";")) {
 121                     File f = new File(dirString.replace("\"", ""),
 122                             TOOL_INNO_SETUP_COMPILER);
 123                     if (f.isFile()) {
 124                         return f.toString();
 125                     }
 126                 }
 127                 return null;
 128             },
 129             null);
 130 
 131     @Override
 132     public String getName() {
 133         return getString("exe.bundler.name");
 134     }


 339                 }
 340             }
 341         }
 342 
 343         return true;
 344     }
 345 
 346     public File bundle(Map<String, ? super Object> p, File outdir)
 347             throws PackagerException {
 348         if (!outdir.isDirectory() && !outdir.mkdirs()) {
 349             throw new PackagerException("error.cannot-create-output-dir",
 350                     outdir.getAbsolutePath());
 351         }
 352         if (!outdir.canWrite()) {
 353             throw new PackagerException("error.cannot-write-to-output-dir",
 354                     outdir.getAbsolutePath());
 355         }
 356 
 357         String tempDirectory = WindowsDefender.getUserTempDirectory();
 358         if (Arguments.CLIOptions.context().userProvidedBuildRoot) {
 359             tempDirectory = TEMP_ROOT.fetchFrom(p).getAbsolutePath();
 360         }
 361         if (WindowsDefender.isThereAPotentialWindowsDefenderIssue(
 362                 tempDirectory)) {
 363             Log.error(MessageFormat.format(
 364                     getString("message.potential.windows.defender.issue"),
 365                     tempDirectory));
 366         }
 367 
 368         // validate we have valid tools before continuing
 369         String iscc = TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(p);
 370         if (iscc == null || !new File(iscc).isFile()) {
 371             Log.verbose(getString("error.iscc-not-found"));
 372             Log.verbose(MessageFormat.format(
 373                     getString("message.iscc-file-string"), iscc));
 374             throw new PackagerException("error.iscc-not-found");
 375         }
 376 
 377         File imageDir = EXE_IMAGE_DIR.fetchFrom(p);
 378         try {
 379             imageDir.mkdirs();


 504         data.put("ARCHITECTURE_BIT_MODE", "x64");
 505 
 506         validateValueAndPut(data, "RUN_FILENAME", APP_NAME, p);
 507 
 508         validateValueAndPut(data, "APPLICATION_DESCRIPTION",
 509                 DESCRIPTION, p);
 510 
 511         data.put("APPLICATION_SERVICE", "returnFalse");
 512         data.put("APPLICATION_NOT_SERVICE", "returnFalse");
 513         data.put("APPLICATION_APP_CDS_INSTALL", "returnFalse");
 514         data.put("START_ON_INSTALL", "");
 515         data.put("STOP_ON_UNINSTALL", "");
 516         data.put("RUN_AT_STARTUP", "");
 517 
 518         String imagePathString =
 519                 WIN_APP_IMAGE.fetchFrom(p).toPath().toAbsolutePath().toString();
 520         data.put("APPLICATION_IMAGE", innosetupEscape(imagePathString));
 521         Log.verbose("setting APPLICATION_IMAGE to " +
 522                 innosetupEscape(imagePathString) + " for InnoSetup");
 523 
 524         StringBuilder addLaunchersCfg = new StringBuilder();
 525         for (Map<String, ? super Object>
 526                 launcher : ADD_LAUNCHERS.fetchFrom(p)) {
 527             String application_name = APP_NAME.fetchFrom(launcher);
 528             if (MENU_HINT.fetchFrom(launcher)) {
 529                 // Name: "{group}\APPLICATION_NAME";
 530                 // Filename: "{app}\APPLICATION_NAME.exe";
 531                 // IconFilename: "{app}\APPLICATION_NAME.ico"
 532                 addLaunchersCfg.append("Name: \"{group}\\");
 533                 addLaunchersCfg.append(application_name);
 534                 addLaunchersCfg.append("\"; Filename: \"{app}\\");
 535                 addLaunchersCfg.append(application_name);
 536                 addLaunchersCfg.append(".exe\"; IconFilename: \"{app}\\");
 537                 addLaunchersCfg.append(application_name);
 538                 addLaunchersCfg.append(".ico\"\r\n");
 539             }
 540             if (SHORTCUT_HINT.fetchFrom(launcher)) {
 541                 // Name: "{commondesktop}\APPLICATION_NAME";
 542                 // Filename: "{app}\APPLICATION_NAME.exe";
 543                 // IconFilename: "{app}\APPLICATION_NAME.ico"
 544                 addLaunchersCfg.append("Name: \"{commondesktop}\\");
 545                 addLaunchersCfg.append(application_name);
 546                 addLaunchersCfg.append("\"; Filename: \"{app}\\");
 547                 addLaunchersCfg.append(application_name);
 548                 addLaunchersCfg.append(".exe\";  IconFilename: \"{app}\\");
 549                 addLaunchersCfg.append(application_name);
 550                 addLaunchersCfg.append(".ico\"\r\n");
 551             }
 552         }
 553         data.put("ADD_LAUNCHERS", addLaunchersCfg.toString());
 554 
 555         StringBuilder registryEntries = new StringBuilder();
 556         String regName = APP_REGISTRY_NAME.fetchFrom(p);
 557         List<Map<String, ? super Object>> fetchFrom =
 558                 FILE_ASSOCIATIONS.fetchFrom(p);
 559         for (int i = 0; i < fetchFrom.size(); i++) {
 560             Map<String, ? super Object> fileAssociation = fetchFrom.get(i);
 561             String description = FA_DESCRIPTION.fetchFrom(fileAssociation);
 562             File icon = FA_ICON.fetchFrom(fileAssociation); //TODO FA_ICON_ICO
 563 
 564             List<String> extensions = FA_EXTENSIONS.fetchFrom(fileAssociation);
 565             String entryName = regName + "File";
 566             if (i > 0) {
 567                 entryName += "." + i;
 568             }
 569 
 570             if (extensions == null) {
 571                 Log.verbose(getString(
 572                         "message.creating-association-with-null-extension"));
 573             } else {


 688                         .append(APP_NAME.fetchFrom(p))
 689                         .append("\"\" \"\"%1\"\"\"\r\n");
 690             } else {
 691                 registryEntries.append(
 692                         "Root: HKCU; Subkey: \"Software\\Classes\\")
 693                         .append(entryName)
 694                         .append("\\shell\\open\\command\"; ValueType: " +
 695                         "string; ValueName: \"\"; ValueData: \"\"\"{app}\\")
 696                         .append(APP_NAME.fetchFrom(p))
 697                         .append("\"\" \"\"%1\"\"\"\r\n");
 698             }
 699         }
 700         if (registryEntries.length() > 0) {
 701             data.put("FILE_ASSOCIATIONS",
 702                     "ChangesAssociations=yes\r\n\r\n[Registry]\r\n" +
 703                     registryEntries.toString());
 704         } else {
 705             data.put("FILE_ASSOCIATIONS", "");
 706         }
 707 
 708         String iss = StandardBundlerParam.isRuntimeInstaller(p) ?

 709                 DEFAULT_JRE_EXE_TEMPLATE : DEFAULT_EXE_PROJECT_TEMPLATE;
 710 
 711         Writer w = new BufferedWriter(new FileWriter(
 712                 getConfig_ExeProjectFile(p)));
 713 
 714         String content = preprocessTextResource(
 715                 getConfig_ExeProjectFile(p).getName(),
 716                 getString("resource.inno-setup-project-file"),
 717                 iss, data, VERBOSE.fetchFrom(p),
 718                 RESOURCE_DIR.fetchFrom(p));
 719         w.write(content);
 720         w.close();
 721         return true;
 722     }
 723 
 724     private final static String removeQuotes(String s) {
 725         if (s.length() > 2 && s.startsWith("\"") && s.endsWith("\"")) {
 726             // special case for '"XXX"' return 'XXX' not '-XXX-'
 727             // note '"' and '""' are excluded from this special case
 728             s = s.substring(1, s.length() - 1);


< prev index next >