+ Path targetJarP;
+ if (bundle.startsWith(LIB_)) {// OS-specific
+ Objects.requireNonNull(nativeTargetA2);
+ if (bundle.startsWith(LIB_ + NATIVE_) // portable native
+ || bundle.startsWith(LIB_ + targetOs + "/" + NATIVE_)) {// OS-specific native
+ targetJarP = nativeTargetA2.resolve(category).resolve(jarP.getFileName());
+ } else if (bundle.startsWith(LIB_ + targetOs)) {// OS-specific portable
+ targetJarP = targetA2.resolve(category).resolve(jarP.getFileName());
+ } else { // ignore other OS
+ continue bundles;
+ }
+ } else {
+ targetJarP = targetA2.resolve(a2Output.relativize(jarP));
+ }
+
+ if (uninstall) { // uninstall
+ if (Files.exists(targetJarP)) {
+ Files.delete(targetJarP);
+ logger.log(DEBUG, "Removed " + targetJarP);
+ count++;
+ }
+ Path targetParent = targetJarP.getParent();
+ if (targetParent.startsWith(targetA2))
+ deleteEmptyParents(targetA2, targetParent);
+ if (nativeTargetA2 != null && targetParent.startsWith(nativeTargetA2))
+ deleteEmptyParents(nativeTargetA2, targetParent);
+ } else { // install
+ Files.createDirectories(targetJarP.getParent());
+ boolean update = Files.exists(targetJarP);
+ Files.copy(jarP, targetJarP, StandardCopyOption.REPLACE_EXISTING);
+ logger.log(DEBUG, (update ? "Updated " : "Installed ") + targetJarP);
+ count++;
+ }
+ }
+ logger.log(INFO, uninstall ? count + " bundles removed" : count + " bundles installed or updated");
+ }
+
+ /** Extracts an argument which must be unique. */
+ String singleArg(Map<String, List<String>> options, String arg, boolean mandatory) {
+ List<String> values = options.get(arg);
+ if (values == null || values.size() == 0)
+ if (mandatory)
+ throw new IllegalArgumentException(arg + " argument must be set");
+ else
+ return null;
+ if (values.size() != 1)
+ throw new IllegalArgumentException("One and only one " + arg + " arguments must be specified");
+ return values.get(0);
+ }
+
+ /** Extracts an argument which can have multiple values. */
+ List<String> multiArg(Map<String, List<String>> options, String arg, boolean mandatory) {
+ List<String> values = options.get(arg);
+ if (mandatory && values == null)
+ throw new IllegalArgumentException(arg + " argument must be set");
+ return values != null ? values : new ArrayList<>();
+ }
+
+ /** Delete empty parent directory up to the base directory (included). */
+ void deleteEmptyParents(Path baseDir, Path targetParent) throws IOException {
+ if (!targetParent.startsWith(baseDir))
+ throw new IllegalArgumentException(targetParent + " does not start with " + baseDir);
+ if (!Files.exists(baseDir))
+ return;
+ if (!Files.exists(targetParent)) {
+ deleteEmptyParents(baseDir, targetParent.getParent());
+ return;
+ }
+ if (!Files.isDirectory(targetParent))
+ throw new IllegalArgumentException(targetParent + " must be a directory");
+ boolean isA2target = Files.isSameFile(baseDir, targetParent);
+ if (!Files.list(targetParent).iterator().hasNext()) {
+ Files.delete(targetParent);
+ if (isA2target)
+ return;// stop after deleting A2 base
+ deleteEmptyParents(baseDir, targetParent.getParent());