src/jdk.jpackage/share/classes/jdk/jpackage/internal/PathGroup.java
branchJDK-8200758-branch
changeset 58994 b09ba68c6a19
parent 58993 b5e1baa9d2c3
child 58995 de1413ae214c
equal deleted inserted replaced
58993:b5e1baa9d2c3 58994:b09ba68c6a19
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    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 package jdk.jpackage.internal;
       
    26 
       
    27 import java.io.File;
       
    28 import java.io.IOException;
       
    29 import java.nio.file.Files;
       
    30 import java.nio.file.Path;
       
    31 import java.util.ArrayList;
       
    32 import java.util.Collection;
       
    33 import java.util.Collections;
       
    34 import java.util.HashMap;
       
    35 import java.util.List;
       
    36 import java.util.Map;
       
    37 import java.util.function.BiFunction;
       
    38 import java.util.stream.Collectors;
       
    39 import java.util.stream.Stream;
       
    40 
       
    41 
       
    42 /**
       
    43  * Group of paths.
       
    44  * Each path in the group is assigned a unique id.
       
    45  */
       
    46 final class PathGroup {
       
    47     PathGroup(Map<Object, Path> paths) {
       
    48         entries = new HashMap<>(paths);
       
    49     }
       
    50 
       
    51     Path getPath(Object id) {
       
    52         if (id == null) {
       
    53             throw new NullPointerException();
       
    54         }
       
    55         return entries.get(id);
       
    56     }
       
    57 
       
    58     void setPath(Object id, Path path) {
       
    59         if (path != null) {
       
    60             entries.put(id, path);
       
    61         } else {
       
    62             entries.remove(id);
       
    63         }
       
    64     }
       
    65 
       
    66     /**
       
    67      * All configured entries.
       
    68      */
       
    69     List<Path> paths() {
       
    70         return entries.values().stream().collect(Collectors.toList());
       
    71     }
       
    72 
       
    73     /**
       
    74      * Root entries.
       
    75      */
       
    76     List<Path> roots() {
       
    77         // Sort by the number of path components in ascending order.
       
    78         List<Map.Entry<Path, Path>> sorted = normalizedPaths().stream().sorted(
       
    79                 (a, b) -> a.getKey().getNameCount() - b.getKey().getNameCount()).collect(
       
    80                         Collectors.toList());
       
    81 
       
    82         // Returns `true` if `a` is a parent of `b`
       
    83         BiFunction<Map.Entry<Path, Path>, Map.Entry<Path, Path>, Boolean> isParentOrSelf = (a, b) -> {
       
    84             return a == b || b.getKey().startsWith(a.getKey());
       
    85         };
       
    86 
       
    87         return sorted.stream().filter(
       
    88                 v -> v == sorted.stream().sequential().filter(
       
    89                         v2 -> isParentOrSelf.apply(v2, v)).findFirst().get()).map(
       
    90                         v -> v.getValue()).collect(Collectors.toList());
       
    91     }
       
    92 
       
    93     long sizeInBytes() throws IOException {
       
    94         long reply = 0;
       
    95         for (Path dir : roots().stream().filter(f -> Files.isDirectory(f)).collect(
       
    96                 Collectors.toList())) {
       
    97             try (Stream<Path> stream = Files.walk(dir)) {
       
    98                 reply += stream.filter(p -> Files.isRegularFile(p)).mapToLong(
       
    99                         f -> f.toFile().length()).sum();
       
   100             }
       
   101         }
       
   102         return reply;
       
   103     }
       
   104 
       
   105     PathGroup resolveAt(Path root) {
       
   106         return new PathGroup(entries.entrySet().stream().collect(
       
   107                 Collectors.toMap(e -> e.getKey(),
       
   108                         e -> root.resolve(e.getValue()))));
       
   109     }
       
   110 
       
   111     void copy(PathGroup dst) throws IOException {
       
   112         copy(this, dst, null, false);
       
   113     }
       
   114 
       
   115     void move(PathGroup dst) throws IOException {
       
   116         copy(this, dst, null, true);
       
   117     }
       
   118 
       
   119     void transform(PathGroup dst, TransformHandler handler) throws IOException {
       
   120         copy(this, dst, handler, false);
       
   121     }
       
   122 
       
   123     static interface Facade<T> {
       
   124         PathGroup pathGroup();
       
   125 
       
   126         default Collection<Path> paths() {
       
   127             return pathGroup().paths();
       
   128         }
       
   129 
       
   130         default List<Path> roots() {
       
   131             return pathGroup().roots();
       
   132         }
       
   133 
       
   134         default long sizeInBytes() throws IOException {
       
   135             return pathGroup().sizeInBytes();
       
   136         }
       
   137 
       
   138         T resolveAt(Path root);
       
   139 
       
   140         default void copy(Facade<T> dst) throws IOException {
       
   141             pathGroup().copy(dst.pathGroup());
       
   142         }
       
   143 
       
   144         default void move(Facade<T> dst) throws IOException {
       
   145             pathGroup().move(dst.pathGroup());
       
   146         }
       
   147 
       
   148         default void transform(Facade<T> dst, TransformHandler handler) throws
       
   149                 IOException {
       
   150             pathGroup().transform(dst.pathGroup(), handler);
       
   151         }
       
   152     }
       
   153 
       
   154     static interface TransformHandler {
       
   155         public void copyFile(Path src, Path dst) throws IOException;
       
   156         public void createDirectory(Path dir) throws IOException;
       
   157     }
       
   158 
       
   159     private static void copy(PathGroup src, PathGroup dst,
       
   160             TransformHandler handler, boolean move) throws IOException {
       
   161         List<Map.Entry<Path, Path>> copyItems = new ArrayList<>();
       
   162         List<Path> excludeItems = new ArrayList<>();
       
   163 
       
   164         for (var id: src.entries.keySet()) {
       
   165             Path srcPath = src.entries.get(id);
       
   166             if (dst.entries.containsKey(id)) {
       
   167                 copyItems.add(Map.entry(srcPath, dst.entries.get(id)));
       
   168             } else {
       
   169                 excludeItems.add(srcPath);
       
   170             }
       
   171         }
       
   172 
       
   173         copy(move, copyItems, excludeItems, handler);
       
   174     }
       
   175 
       
   176     private static void copy(boolean move, List<Map.Entry<Path, Path>> entries,
       
   177             List<Path> excludePaths, TransformHandler handler) throws
       
   178             IOException {
       
   179 
       
   180         if (handler == null) {
       
   181             handler = new TransformHandler() {
       
   182                 @Override
       
   183                 public void copyFile(Path src, Path dst) throws IOException {
       
   184                     Files.createDirectories(dst.getParent());
       
   185                     if (move) {
       
   186                         Files.move(src, dst);
       
   187                     } else {
       
   188                         Files.copy(src, dst);
       
   189                     }
       
   190                 }
       
   191 
       
   192                 @Override
       
   193                 public void createDirectory(Path dir) throws IOException {
       
   194                     Files.createDirectories(dir);
       
   195                 }
       
   196             };
       
   197         }
       
   198 
       
   199         // destination -> source file mapping
       
   200         Map<Path, Path> actions = new HashMap<>();
       
   201         for (var action: entries) {
       
   202             Path src = action.getKey();
       
   203             Path dst = action.getValue();
       
   204             if (src.toFile().isDirectory()) {
       
   205                try (Stream<Path> stream = Files.walk(src)) {
       
   206                    stream.sequential().forEach(path -> actions.put(dst.resolve(
       
   207                             src.relativize(path)).normalize(), path));
       
   208                }
       
   209             } else {
       
   210                 actions.put(dst.normalize(), src);
       
   211             }
       
   212         }
       
   213 
       
   214         for (var action : actions.entrySet()) {
       
   215             Path dst = action.getKey();
       
   216             Path src = action.getValue();
       
   217 
       
   218             if (excludePaths.stream().anyMatch(src::startsWith)) {
       
   219                 continue;
       
   220             }
       
   221 
       
   222             if (src.equals(dst) || !src.toFile().exists()) {
       
   223                 continue;
       
   224             }
       
   225 
       
   226             if (src.toFile().isDirectory()) {
       
   227                 handler.createDirectory(dst);
       
   228             } else {
       
   229                 handler.copyFile(src, dst);
       
   230             }
       
   231         }
       
   232 
       
   233         if (move) {
       
   234             // Delete source dirs.
       
   235             for (var entry: entries) {
       
   236                 File srcFile = entry.getKey().toFile();
       
   237                 if (srcFile.isDirectory()) {
       
   238                     IOUtils.deleteRecursive(srcFile);
       
   239                 }
       
   240             }
       
   241         }
       
   242     }
       
   243 
       
   244     private static Map.Entry<Path, Path> normalizedPath(Path v) {
       
   245         final Path normalized;
       
   246         if (!v.isAbsolute()) {
       
   247             normalized = Path.of("./").resolve(v.normalize());
       
   248         } else {
       
   249             normalized = v.normalize();
       
   250         }
       
   251 
       
   252         return Map.entry(normalized, v);
       
   253     }
       
   254 
       
   255     private List<Map.Entry<Path, Path>> normalizedPaths() {
       
   256         return entries.values().stream().map(PathGroup::normalizedPath).collect(
       
   257                 Collectors.toList());
       
   258     }
       
   259 
       
   260     private final Map<Object, Path> entries;
       
   261 }