View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.buildcache.its;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.LinkOption;
25  import java.nio.file.Path;
26  import java.nio.file.attribute.FileTime;
27  import java.nio.file.attribute.PosixFilePermission;
28  import java.nio.file.attribute.PosixFilePermissions;
29  import java.time.Instant;
30  import java.time.ZoneId;
31  import java.time.ZonedDateTime;
32  import java.time.format.DateTimeFormatter;
33  import java.util.EnumSet;
34  import java.util.Map;
35  import java.util.Objects;
36  import java.util.Set;
37  import java.util.TreeMap;
38  
39  public class PathEntry implements Comparable<PathEntry> {
40  
41      private final Path abs;
42      private final Path path;
43      private final Map<String, Object> attributes;
44  
45      public PathEntry(Path abs, Path root) {
46          this.abs = abs;
47          this.path = abs.startsWith(root) ? root.relativize(abs) : abs;
48          this.attributes = readAttributes(abs);
49      }
50  
51      @Override
52      public int compareTo(PathEntry o) {
53          return path.toString().compareTo(o.path.toString());
54      }
55  
56      boolean isNotDirectory() {
57          return is("isRegularFile") || is("isSymbolicLink") || is("isOther");
58      }
59  
60      boolean isDirectory() {
61          return is("isDirectory");
62      }
63  
64      private boolean is(String attr) {
65          Object d = attributes.get(attr);
66          return d instanceof Boolean && (Boolean) d;
67      }
68  
69      String display() {
70          String suffix;
71          String link = "";
72          if (is("isSymbolicLink")) {
73              suffix = "@";
74              try {
75                  Path l = Files.readSymbolicLink(abs);
76                  link = " -> " + l.toString();
77              } catch (IOException e) {
78                  // ignore
79              }
80          } else if (is("isDirectory")) {
81              suffix = "/";
82          } else if (is("isExecutable")) {
83              suffix = "*";
84          } else if (is("isOther")) {
85              suffix = "";
86          } else {
87              suffix = "";
88          }
89          return path.toString() + suffix + link;
90      }
91  
92      String longDisplay() {
93          String username = getUsername();
94          String group = getGroup();
95          Number length = (Number) attributes.get("size");
96          if (length == null) {
97              length = 0L;
98          }
99          String lengthString = formatLength(length);
100         @SuppressWarnings("unchecked")
101         Set<PosixFilePermission> perms = (Set<PosixFilePermission>) attributes.get("permissions");
102         if (perms == null) {
103             perms = EnumSet.noneOf(PosixFilePermission.class);
104         }
105         return (is("isDirectory") ? "d" : (is("isSymbolicLink") ? "l" : (is("isOther") ? "o" : "-")))
106                 + PosixFilePermissions.toString(perms) + " "
107                 + String.format(
108                         "%3s",
109                         (attributes.containsKey("nlink")
110                                 ? attributes.get("nlink").toString()
111                                 : "1"))
112                 + " " + username + " " + group + " " + lengthString + " "
113                 + toString((FileTime) attributes.get("lastModifiedTime"))
114                 + " " + display();
115     }
116 
117     private String getUsername() {
118         String username = attributes.containsKey("owner") ? Objects.toString(attributes.get("owner"), null) : "owner";
119         if (username.length() > 8) {
120             username = username.substring(0, 8);
121         } else {
122             username = String.format("%-8s", username);
123         }
124         return username;
125     }
126 
127     private String getGroup() {
128         String group = attributes.containsKey("group") ? Objects.toString(attributes.get("group"), null) : "group";
129         if (group.length() > 8) {
130             group = group.substring(0, 8);
131         } else {
132             group = String.format("%-8s", group);
133         }
134         return group;
135     }
136 
137     private String formatLength(Number length) {
138         double l = length.longValue();
139         String unit = "B";
140         if (l >= 1000) {
141             l /= 1024;
142             unit = "K";
143             if (l >= 1000) {
144                 l /= 1024;
145                 unit = "M";
146                 if (l >= 1000) {
147                     l /= 1024;
148                     unit = "T";
149                 }
150             }
151         }
152         if (l < 10 && length.longValue() > 1000) {
153             return String.format("%.1f%s", l, unit);
154         } else {
155             return String.format("%3.0f%s", l, unit);
156         }
157     }
158 
159     protected String toString(FileTime time) {
160         long millis = (time != null) ? time.toMillis() : -1L;
161         if (millis < 0L) {
162             return "------------";
163         }
164         ZonedDateTime dt = Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault());
165         if (System.currentTimeMillis() - millis < 183L * 24L * 60L * 60L * 1000L) {
166             return DateTimeFormatter.ofPattern("MMM ppd HH:mm").format(dt);
167         } else {
168             return DateTimeFormatter.ofPattern("MMM ppd  yyyy").format(dt);
169         }
170     }
171 
172     protected Map<String, Object> readAttributes(Path path) {
173         Map<String, Object> attrs = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
174         for (String view : path.getFileSystem().supportedFileAttributeViews()) {
175             try {
176                 Map<String, Object> ta = Files.readAttributes(path, view + ":*", LinkOption.NOFOLLOW_LINKS);
177                 ta.forEach(attrs::putIfAbsent);
178             } catch (IOException e) {
179                 // Ignore
180             }
181         }
182         attrs.computeIfAbsent("isExecutable", s -> Files.isExecutable(path));
183         attrs.computeIfAbsent("permissions", s -> getPermissionsFromFile(path.toFile()));
184         return attrs;
185     }
186 
187     static Set<PosixFilePermission> getPermissionsFromFile(File f) {
188         Set<PosixFilePermission> perms = EnumSet.noneOf(PosixFilePermission.class);
189         if (f.canRead()) {
190             perms.add(PosixFilePermission.OWNER_READ);
191             perms.add(PosixFilePermission.GROUP_READ);
192             perms.add(PosixFilePermission.OTHERS_READ);
193         }
194         if (f.canWrite()) {
195             perms.add(PosixFilePermission.OWNER_WRITE);
196             perms.add(PosixFilePermission.GROUP_WRITE);
197             perms.add(PosixFilePermission.OTHERS_WRITE);
198         }
199         if (f.canExecute()) {
200             perms.add(PosixFilePermission.OWNER_EXECUTE);
201             perms.add(PosixFilePermission.GROUP_EXECUTE);
202             perms.add(PosixFilePermission.OTHERS_EXECUTE);
203         }
204         return perms;
205     }
206 }