15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.oracle.tools.packager;
27
28 import com.sun.javafx.tools.packager.bundlers.BundleParams;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.StringReader;
33 import java.nio.file.Files;
34 import java.text.MessageFormat;
35 import java.util.*;
36 import java.util.function.BiFunction;
37 import java.util.function.Function;
38 import java.util.jar.Attributes;
39 import java.util.jar.JarFile;
40 import java.util.jar.Manifest;
41 import java.util.regex.Pattern;
42
43 public class StandardBundlerParam<T> extends BundlerParamInfo<T> {
44
45 public static final String MANIFEST_JAVAFX_MAIN ="JavaFX-Application-Class";
46 public static final String MANIFEST_PRELOADER = "JavaFX-Preloader-Class";
47
48 private static final ResourceBundle I18N =
49 ResourceBundle.getBundle(StandardBundlerParam.class.getName());
50
51 public StandardBundlerParam(String name, String description, String id,
52 Class<T> valueType,
53 Function<Map<String, ? super Object>, T> defaultValueFunction,
54 BiFunction<String, Map<String, ? super Object>, T> stringConverter) {
55 this.name = name;
60 this.stringConverter = stringConverter;
61 }
62
63 public static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
64 new StandardBundlerParam<>(
65 I18N.getString("param.app-resources.name"),
66 I18N.getString("param.app-resource.description"),
67 BundleParams.PARAM_APP_RESOURCES,
68 RelativeFileSet.class,
69 null, // no default. Required parameter
70 null // no string translation, tool must provide complex type
71 );
72
73 @SuppressWarnings("unchecked")
74 public static final StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST =
75 new StandardBundlerParam<>(
76 I18N.getString("param.app-resources-list.name"),
77 I18N.getString("param.app-resource-list.description"),
78 BundleParams.PARAM_APP_RESOURCES + "List",
79 (Class<List<RelativeFileSet>>) (Object) List.class,
80 p -> new ArrayList<>(Arrays.asList(APP_RESOURCES.fetchFrom(p))), // Default is appResources, as a single item list
81 null // no string translation, tool must provide complex type
82 );
83
84 public static final StandardBundlerParam<File> ICON =
85 new StandardBundlerParam<>(
86 I18N.getString("param.icon-file.name"),
87 I18N.getString("param.icon-file.description"),
88 BundleParams.PARAM_ICON,
89 File.class,
90 params -> null,
91 (s, p) -> new File(s)
92 );
93
94
95 public static final StandardBundlerParam<String> MAIN_CLASS =
96 new StandardBundlerParam<>(
97 I18N.getString("param.main-class.name"),
98 I18N.getString("param.main-class.description"),
99 BundleParams.PARAM_APPLICATION_CLASS,
100 String.class,
101 params -> {
102 extractMainClassInfoFromAppResources(params);
103 return (String) params.get(BundleParams.PARAM_APPLICATION_CLASS);
104 },
105 (s, p) -> s
106 );
107
108 public static final StandardBundlerParam<String> APP_NAME =
109 new StandardBundlerParam<>(
110 I18N.getString("param.app-name.name"),
111 I18N.getString("param.app-name.description"),
112 BundleParams.PARAM_NAME,
113 String.class,
114 params -> {
115 String s = MAIN_CLASS.fetchFrom(params);
116 if (s == null) return null;
117
118 int idx = s.lastIndexOf(".");
119 if (idx >= 0) {
120 return s.substring(idx+1);
121 }
178 params -> MessageFormat.format(I18N.getString("param.copyright.default"), new Date()),
179 (s, p) -> s
180 );
181
182 // note that each bundler is likely to replace this one with their own converter
183 public static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
184 new StandardBundlerParam<>(
185 I18N.getString("param.main-jar.name"),
186 I18N.getString("param.main-jar.description"),
187 "mainJar",
188 RelativeFileSet.class,
189 params -> {
190 extractMainClassInfoFromAppResources(params);
191 return (RelativeFileSet) params.get("mainJar");
192 },
193 (s, p) -> {
194 for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(p)) {
195 File appResourcesRoot = rfs.getBaseDirectory();
196 File f = new File(appResourcesRoot, s);
197 if (f.exists()) {
198 return new RelativeFileSet(appResourcesRoot, new LinkedHashSet<>(Arrays.asList(f)));
199 }
200 }
201 throw new IllegalArgumentException(
202 new ConfigException(
203 MessageFormat.format(I18N.getString("error.main-jar-does-not-exist"), s),
204 I18N.getString("error.main-jar-does-not-exist.advice")));
205 }
206 );
207
208 public static final StandardBundlerParam<String> CLASSPATH =
209 new StandardBundlerParam<>(
210 I18N.getString("param.classpath.name"),
211 I18N.getString("param.classpath.description"),
212 "classpath",
213 String.class,
214 params -> {
215 extractMainClassInfoFromAppResources(params);
216 String cp = (String) params.get("classpath");
217 return cp == null ? "" : cp;
218 },
402 I18N.getString("param.menu-shortcut-hint.description"),
403 BundleParams.PARAM_MENU,
404 Boolean.class,
405 params -> true,
406 // valueOf(null) is false, and we actually do want null in some cases
407 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
408 );
409
410 @SuppressWarnings("unchecked")
411 public static final StandardBundlerParam<List<String>> LICENSE_FILE =
412 new StandardBundlerParam<>(
413 I18N.getString("param.license-file.name"),
414 I18N.getString("param.license-file.description"),
415 BundleParams.PARAM_LICENSE_FILE,
416 (Class<List<String>>)(Object)List.class,
417 params -> Collections.<String>emptyList(),
418 (s, p) -> Arrays.asList(s.split(","))
419 );
420
421 public static final BundlerParamInfo<String> LICENSE_TYPE =
422 new StandardBundlerParam<> (
423 I18N.getString("param.license-type.name"),
424 I18N.getString("param.license-type.description"),
425 BundleParams.PARAM_LICENSE_TYPE,
426 String.class,
427 params -> I18N.getString("param.license-type.default"),
428 (s, p) -> s
429 );
430
431 public static final StandardBundlerParam<File> BUILD_ROOT =
432 new StandardBundlerParam<>(
433 I18N.getString("param.build-root.name"),
434 I18N.getString("param.build-root.description"),
435 "buildRoot",
436 File.class,
437 params -> {
438 try {
439 return Files.createTempDirectory("fxbundler").toFile();
440 } catch (IOException ioe) {
441 return null;
442 }
590 (s, p) -> Boolean.parseBoolean(s)
591 );
592
593 public static final StandardBundlerParam<String> APP_CDS_CACHE_MODE =
594 new StandardBundlerParam<>(
595 I18N.getString("param.com-app-cds-cache-mode.name"),
596 I18N.getString("param.com-app-cds-cache-mode.description"),
597 "commercial.AppCDS.cache",
598 String.class,
599 p -> "auto",
600 (s, p) -> s
601 );
602
603 @SuppressWarnings("unchecked")
604 public static final StandardBundlerParam<List<String>> APP_CDS_CLASS_ROOTS =
605 new StandardBundlerParam<>(
606 I18N.getString("param.com-app-cds-root.name"),
607 I18N.getString("param.com-app-cds-root.description"),
608 "commercial.AppCDS.classRoots",
609 (Class<List<String>>)((Object)List.class),
610 p -> Arrays.asList(MAIN_CLASS.fetchFrom(p)),
611 (s, p) -> Arrays.asList(s.split("[ ,:]"))
612 );
613
614 public static void extractMainClassInfoFromAppResources(Map<String, ? super Object> params) {
615 boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
616 boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
617 boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
618 boolean hasPreloader = params.containsKey(PRELOADER_CLASS.getID());
619
620 if (hasMainClass && hasMainJar && hasMainJarClassPath) {
621 return;
622 }
623 // it's a pair. The [0] is the srcdir [1] is the file relative to sourcedir
624 List<String[]> filesToCheck = new ArrayList<>();
625
626 if (hasMainJar) {
627 RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);
628 for (String s : rfs.getIncludedFiles()) {
629 filesToCheck.add(new String[]{rfs.getBaseDirectory().toString(), s});
630 }
681 continue;
682 }
683 } else {
684 if (fxMain != null) {
685 params.put(USE_FX_PACKAGING.getID(), true);
686 params.put(MAIN_CLASS.getID(), fxMain);
687 } else if (mainClass != null) {
688 params.put(USE_FX_PACKAGING.getID(), false);
689 params.put(MAIN_CLASS.getID(), mainClass);
690 } else {
691 continue;
692 }
693 }
694 if (!hasPreloader && preloaderClass != null) {
695 params.put(PRELOADER_CLASS.getID(), preloaderClass);
696 }
697 if (!hasMainJar) {
698 if (fnames[0] == null) {
699 fnames[0] = file.getParentFile().toString();
700 }
701 params.put(MAIN_JAR.getID(), new RelativeFileSet(new File(fnames[0]), new LinkedHashSet<>(Arrays.asList(file))));
702 }
703 if (!hasMainJarClassPath) {
704 String cp = attrs.getValue(Attributes.Name.CLASS_PATH);
705 params.put(CLASSPATH.getID(), cp == null ? "" : cp);
706 }
707 break;
708 }
709 }
710 } catch (IOException ignore) {
711 ignore.printStackTrace();
712 }
713 }
714 }
715
716 public static void validateMainClassInfoFromAppResources(Map<String, ? super Object> params) throws ConfigException {
717 boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
718 boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
719 boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
720
721 if (hasMainClass && hasMainJar && hasMainJarClassPath) {
746 private static List<String> splitStringWithEscapes(String s) {
747 List<String> l = new ArrayList<>();
748 StringBuilder current = new StringBuilder();
749 boolean quoted = false;
750 boolean escaped = false;
751 for (char c : s.toCharArray()) {
752 if (escaped) {
753 current.append(c);
754 } else if ('"' == c) {
755 quoted = !quoted;
756 } else if (!quoted && Character.isWhitespace(c)) {
757 l.add(current.toString());
758 current = new StringBuilder();
759 } else {
760 current.append(c);
761 }
762 }
763 l.add(current.toString());
764 return l;
765 }
766
767 }
|
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.oracle.tools.packager;
27
28 import com.sun.javafx.tools.packager.bundlers.BundleParams;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.StringReader;
33 import java.nio.file.Files;
34 import java.text.MessageFormat;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.Date;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.LinkedHashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Optional;
45 import java.util.Properties;
46 import java.util.ResourceBundle;
47 import java.util.Set;
48 import java.util.function.BiFunction;
49 import java.util.function.Function;
50 import java.util.jar.Attributes;
51 import java.util.jar.JarFile;
52 import java.util.jar.Manifest;
53 import java.util.regex.Pattern;
54
55 public class StandardBundlerParam<T> extends BundlerParamInfo<T> {
56
57 public static final String MANIFEST_JAVAFX_MAIN ="JavaFX-Application-Class";
58 public static final String MANIFEST_PRELOADER = "JavaFX-Preloader-Class";
59
60 private static final ResourceBundle I18N =
61 ResourceBundle.getBundle(StandardBundlerParam.class.getName());
62
63 public StandardBundlerParam(String name, String description, String id,
64 Class<T> valueType,
65 Function<Map<String, ? super Object>, T> defaultValueFunction,
66 BiFunction<String, Map<String, ? super Object>, T> stringConverter) {
67 this.name = name;
72 this.stringConverter = stringConverter;
73 }
74
75 public static final StandardBundlerParam<RelativeFileSet> APP_RESOURCES =
76 new StandardBundlerParam<>(
77 I18N.getString("param.app-resources.name"),
78 I18N.getString("param.app-resource.description"),
79 BundleParams.PARAM_APP_RESOURCES,
80 RelativeFileSet.class,
81 null, // no default. Required parameter
82 null // no string translation, tool must provide complex type
83 );
84
85 @SuppressWarnings("unchecked")
86 public static final StandardBundlerParam<List<RelativeFileSet>> APP_RESOURCES_LIST =
87 new StandardBundlerParam<>(
88 I18N.getString("param.app-resources-list.name"),
89 I18N.getString("param.app-resource-list.description"),
90 BundleParams.PARAM_APP_RESOURCES + "List",
91 (Class<List<RelativeFileSet>>) (Object) List.class,
92 p -> new ArrayList<>(Collections.singletonList(APP_RESOURCES.fetchFrom(p))), // Default is appResources, as a single item list
93 StandardBundlerParam::createAppResourcesListFromString
94 );
95
96 private static List<RelativeFileSet> createAppResourcesListFromString(String s, Map<String, ? super Object> objectObjectMap) {
97 List<RelativeFileSet> result = new ArrayList<>();
98 for (String path : s.split("[:;]")) {
99 File f = new File(path);
100 if (f.getName().equals("*") || path.endsWith("/") || path.endsWith("\\")) {
101 if (f.getName().equals("*")) {
102 f = f.getParentFile();
103 }
104 Set<File> theFiles = new HashSet<>();
105 try {
106 Files.walk(f.toPath())
107 .filter(Files::isRegularFile)
108 .forEach(p -> theFiles.add(p.toFile()));
109 } catch (IOException e) {
110 e.printStackTrace();
111 }
112 result.add(new RelativeFileSet(f, theFiles));
113 } else {
114 result.add(new RelativeFileSet(f.getParentFile(), Collections.singleton(f)));
115 }
116 }
117 return result;
118 }
119
120 public static final StandardBundlerParam<File> ICON =
121 new StandardBundlerParam<>(
122 I18N.getString("param.icon-file.name"),
123 I18N.getString("param.icon-file.description"),
124 BundleParams.PARAM_ICON,
125 File.class,
126 params -> null,
127 (s, p) -> new File(s)
128 );
129
130
131 public static final StandardBundlerParam<String> MAIN_CLASS =
132 new StandardBundlerParam<>(
133 I18N.getString("param.main-class.name"),
134 I18N.getString("param.main-class.description"),
135 BundleParams.PARAM_APPLICATION_CLASS,
136 String.class,
137 params -> {
138 //FIXME sniff modules
139 extractMainClassInfoFromAppResources(params);
140 return (String) params.get(BundleParams.PARAM_APPLICATION_CLASS);
141 },
142 (s, p) -> s
143 );
144
145 public static final StandardBundlerParam<String> APP_NAME =
146 new StandardBundlerParam<>(
147 I18N.getString("param.app-name.name"),
148 I18N.getString("param.app-name.description"),
149 BundleParams.PARAM_NAME,
150 String.class,
151 params -> {
152 String s = MAIN_CLASS.fetchFrom(params);
153 if (s == null) return null;
154
155 int idx = s.lastIndexOf(".");
156 if (idx >= 0) {
157 return s.substring(idx+1);
158 }
215 params -> MessageFormat.format(I18N.getString("param.copyright.default"), new Date()),
216 (s, p) -> s
217 );
218
219 // note that each bundler is likely to replace this one with their own converter
220 public static final StandardBundlerParam<RelativeFileSet> MAIN_JAR =
221 new StandardBundlerParam<>(
222 I18N.getString("param.main-jar.name"),
223 I18N.getString("param.main-jar.description"),
224 "mainJar",
225 RelativeFileSet.class,
226 params -> {
227 extractMainClassInfoFromAppResources(params);
228 return (RelativeFileSet) params.get("mainJar");
229 },
230 (s, p) -> {
231 for (RelativeFileSet rfs : APP_RESOURCES_LIST.fetchFrom(p)) {
232 File appResourcesRoot = rfs.getBaseDirectory();
233 File f = new File(appResourcesRoot, s);
234 if (f.exists()) {
235 return new RelativeFileSet(appResourcesRoot, new LinkedHashSet<>(Collections.singletonList(f)));
236 }
237 }
238 throw new IllegalArgumentException(
239 new ConfigException(
240 MessageFormat.format(I18N.getString("error.main-jar-does-not-exist"), s),
241 I18N.getString("error.main-jar-does-not-exist.advice")));
242 }
243 );
244
245 public static final StandardBundlerParam<String> CLASSPATH =
246 new StandardBundlerParam<>(
247 I18N.getString("param.classpath.name"),
248 I18N.getString("param.classpath.description"),
249 "classpath",
250 String.class,
251 params -> {
252 extractMainClassInfoFromAppResources(params);
253 String cp = (String) params.get("classpath");
254 return cp == null ? "" : cp;
255 },
439 I18N.getString("param.menu-shortcut-hint.description"),
440 BundleParams.PARAM_MENU,
441 Boolean.class,
442 params -> true,
443 // valueOf(null) is false, and we actually do want null in some cases
444 (s, p) -> (s == null || "null".equalsIgnoreCase(s))? true : Boolean.valueOf(s)
445 );
446
447 @SuppressWarnings("unchecked")
448 public static final StandardBundlerParam<List<String>> LICENSE_FILE =
449 new StandardBundlerParam<>(
450 I18N.getString("param.license-file.name"),
451 I18N.getString("param.license-file.description"),
452 BundleParams.PARAM_LICENSE_FILE,
453 (Class<List<String>>)(Object)List.class,
454 params -> Collections.<String>emptyList(),
455 (s, p) -> Arrays.asList(s.split(","))
456 );
457
458 public static final BundlerParamInfo<String> LICENSE_TYPE =
459 new StandardBundlerParam<>(
460 I18N.getString("param.license-type.name"),
461 I18N.getString("param.license-type.description"),
462 BundleParams.PARAM_LICENSE_TYPE,
463 String.class,
464 params -> I18N.getString("param.license-type.default"),
465 (s, p) -> s
466 );
467
468 public static final StandardBundlerParam<File> BUILD_ROOT =
469 new StandardBundlerParam<>(
470 I18N.getString("param.build-root.name"),
471 I18N.getString("param.build-root.description"),
472 "buildRoot",
473 File.class,
474 params -> {
475 try {
476 return Files.createTempDirectory("fxbundler").toFile();
477 } catch (IOException ioe) {
478 return null;
479 }
627 (s, p) -> Boolean.parseBoolean(s)
628 );
629
630 public static final StandardBundlerParam<String> APP_CDS_CACHE_MODE =
631 new StandardBundlerParam<>(
632 I18N.getString("param.com-app-cds-cache-mode.name"),
633 I18N.getString("param.com-app-cds-cache-mode.description"),
634 "commercial.AppCDS.cache",
635 String.class,
636 p -> "auto",
637 (s, p) -> s
638 );
639
640 @SuppressWarnings("unchecked")
641 public static final StandardBundlerParam<List<String>> APP_CDS_CLASS_ROOTS =
642 new StandardBundlerParam<>(
643 I18N.getString("param.com-app-cds-root.name"),
644 I18N.getString("param.com-app-cds-root.description"),
645 "commercial.AppCDS.classRoots",
646 (Class<List<String>>)((Object)List.class),
647 p -> Collections.singletonList(MAIN_CLASS.fetchFrom(p)),
648 (s, p) -> Arrays.asList(s.split("[ ,:]"))
649 );
650
651 public static void extractMainClassInfoFromAppResources(Map<String, ? super Object> params) {
652 boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
653 boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
654 boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
655 boolean hasPreloader = params.containsKey(PRELOADER_CLASS.getID());
656
657 if (hasMainClass && hasMainJar && hasMainJarClassPath) {
658 return;
659 }
660 // it's a pair. The [0] is the srcdir [1] is the file relative to sourcedir
661 List<String[]> filesToCheck = new ArrayList<>();
662
663 if (hasMainJar) {
664 RelativeFileSet rfs = MAIN_JAR.fetchFrom(params);
665 for (String s : rfs.getIncludedFiles()) {
666 filesToCheck.add(new String[]{rfs.getBaseDirectory().toString(), s});
667 }
718 continue;
719 }
720 } else {
721 if (fxMain != null) {
722 params.put(USE_FX_PACKAGING.getID(), true);
723 params.put(MAIN_CLASS.getID(), fxMain);
724 } else if (mainClass != null) {
725 params.put(USE_FX_PACKAGING.getID(), false);
726 params.put(MAIN_CLASS.getID(), mainClass);
727 } else {
728 continue;
729 }
730 }
731 if (!hasPreloader && preloaderClass != null) {
732 params.put(PRELOADER_CLASS.getID(), preloaderClass);
733 }
734 if (!hasMainJar) {
735 if (fnames[0] == null) {
736 fnames[0] = file.getParentFile().toString();
737 }
738 params.put(MAIN_JAR.getID(), new RelativeFileSet(new File(fnames[0]), new LinkedHashSet<>(Collections.singletonList(file))));
739 }
740 if (!hasMainJarClassPath) {
741 String cp = attrs.getValue(Attributes.Name.CLASS_PATH);
742 params.put(CLASSPATH.getID(), cp == null ? "" : cp);
743 }
744 break;
745 }
746 }
747 } catch (IOException ignore) {
748 ignore.printStackTrace();
749 }
750 }
751 }
752
753 public static void validateMainClassInfoFromAppResources(Map<String, ? super Object> params) throws ConfigException {
754 boolean hasMainClass = params.containsKey(MAIN_CLASS.getID());
755 boolean hasMainJar = params.containsKey(MAIN_JAR.getID());
756 boolean hasMainJarClassPath = params.containsKey(CLASSPATH.getID());
757
758 if (hasMainClass && hasMainJar && hasMainJarClassPath) {
783 private static List<String> splitStringWithEscapes(String s) {
784 List<String> l = new ArrayList<>();
785 StringBuilder current = new StringBuilder();
786 boolean quoted = false;
787 boolean escaped = false;
788 for (char c : s.toCharArray()) {
789 if (escaped) {
790 current.append(c);
791 } else if ('"' == c) {
792 quoted = !quoted;
793 } else if (!quoted && Character.isWhitespace(c)) {
794 l.add(current.toString());
795 current = new StringBuilder();
796 } else {
797 current.append(c);
798 }
799 }
800 l.add(current.toString());
801 return l;
802 }
803 }
|