1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * SteelBlue: DCI User Interfaces
5 * http://tidalwave.it/projects/steelblue
6 *
7 * Copyright (C) 2015 - 2024 by Tidalwave s.a.s. (http://tidalwave.it)
8 *
9 * *************************************************************************************************************************************************************
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18 *
19 * *************************************************************************************************************************************************************
20 *
21 * git clone https://bitbucket.org/tidalwave/steelblue-src
22 * git clone https://github.com/tidalwave-it/steelblue-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.role.ui.example.model;
27
28 import javax.annotation.Nonnegative;
29 import javax.annotation.Nonnull;
30 import javax.annotation.concurrent.Immutable;
31 import java.time.ZoneId;
32 import java.time.ZonedDateTime;
33 import java.util.Comparator;
34 import java.util.List;
35 import java.util.Optional;
36 import java.io.IOException;
37 import java.io.UncheckedIOException;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.nio.file.attribute.BasicFileAttributeView;
41 import java.nio.file.attribute.BasicFileAttributes;
42 import java.nio.file.attribute.FileTime;
43 import it.tidalwave.util.As;
44 import it.tidalwave.util.spi.SimpleFinderSupport;
45 import it.tidalwave.role.SimpleComposite;
46 import it.tidalwave.role.ui.Displayable;
47 import lombok.EqualsAndHashCode;
48 import lombok.RequiredArgsConstructor;
49 import lombok.ToString;
50 import lombok.experimental.Delegate;
51 import lombok.extern.slf4j.Slf4j;
52 import static java.util.Collections.emptyList;
53 import static java.util.stream.Collectors.*;
54
55 /***************************************************************************************************************************************************************
56 *
57 * A class that models a file with its attributes and children.
58 *
59 * @author Fabrizio Giudici
60 *
61 **************************************************************************************************************************************************************/
62 @Immutable @EqualsAndHashCode @ToString @Slf4j
63 public class FileEntity implements As, Displayable
64 {
65 @RequiredArgsConstructor
66 private static class FileEntityFinder extends SimpleFinderSupport<FileEntity>
67 {
68 private static final long serialVersionUID = 5780394869213L;
69
70 @Nonnull
71 private final Path path;
72
73 public FileEntityFinder (@Nonnull final FileEntityFinder other, @Nonnull final Object override)
74 {
75 super(other, override);
76 final var source = getSource(FileEntityFinder.class, other, override);
77 this.path = source.path;
78 }
79
80 @Nonnull @Override
81 protected List<FileEntity> computeNeededResults()
82 {
83 try (final var stream = Files.list(path))
84 {
85 return stream.sorted(Comparator.comparing(Path::toString)).map(FileEntity::of).collect(toList());
86 }
87 catch (IOException e)
88 {
89 log.error("While listing directory " + path, e);
90 throw new UncheckedIOException(e);
91 }
92 }
93 }
94
95 /** The path of the file.*/
96 @Nonnull
97 private final Path path;
98
99 /** Support object for implementing {@link As} functions.*/
100 @Delegate
101 private final As delegate;
102
103 /***********************************************************************************************************************************************************
104 * Creates a new instance related to the given path.
105 * @param path the path
106 **********************************************************************************************************************************************************/
107 // START SNIPPET: constructor
108 private FileEntity (@Nonnull final Path path)
109 {
110 this.path = path;
111 delegate = As.forObject(this, Files.isDirectory(path) ? List.of(SimpleComposite.of(new FileEntityFinder(path))) : emptyList());
112 }
113 // END SNIPPET: constructor
114
115 /***********************************************************************************************************************************************************
116 * {@return a new instance} related to the given path.
117 * @param path the path
118 **********************************************************************************************************************************************************/
119 // START SNIPPET: constructor
120 @Nonnull
121 public static FileEntity of (@Nonnull final Path path)
122 {
123 return new FileEntity(path);
124 }
125 // END SNIPPET: constructor
126
127 /***********************************************************************************************************************************************************
128 * {@return the display name}. It's a static implementation of the {@link Displayable} role.
129 **********************************************************************************************************************************************************/
130 @Override @Nonnull
131 public String getDisplayName()
132 {
133 return Optional.ofNullable(path.getFileName()).map(Path::toString).orElse("/");
134 }
135
136 /***********************************************************************************************************************************************************
137 * {@return the creation date-time} of the file.
138 * @throws IOException if the file does not exist or cannot be accessed
139 **********************************************************************************************************************************************************/
140 @Nonnull
141 public ZonedDateTime getCreationDateTime()
142 throws IOException
143 {
144 return toZoneDateTime(getBasicFileAttributes().creationTime());
145 }
146
147 /***********************************************************************************************************************************************************
148 * {@return the last access date-time} of the file.
149 * @throws IOException if the file does not exist or cannot be accessed
150 **********************************************************************************************************************************************************/
151 @Nonnull
152 public ZonedDateTime getLastAccessDateTime()
153 throws IOException
154 {
155 return toZoneDateTime(getBasicFileAttributes().lastAccessTime());
156 }
157
158 /***********************************************************************************************************************************************************
159 * {@return the last modified date-time} of the file.
160 * @throws IOException if the file does not exist or cannot be accessed
161 **********************************************************************************************************************************************************/
162 @Nonnull
163 public ZonedDateTime getLastModifiedDateTime()
164 throws IOException
165 {
166 return toZoneDateTime(getBasicFileAttributes().lastModifiedTime());
167 }
168
169 /***********************************************************************************************************************************************************
170 * {@return the size} of the file.
171 * @throws IOException if the file does not exist or cannot be accessed
172 **********************************************************************************************************************************************************/
173 @Nonnegative
174 public long getSize()
175 throws IOException
176 {
177 return Files.size(path);
178 }
179
180 /***********************************************************************************************************************************************************
181 * {@return the basic attributes} of the file.
182 * @throws IOException if the file does not exist or cannot be accessed
183 **********************************************************************************************************************************************************/
184 @Nonnull
185 private BasicFileAttributes getBasicFileAttributes()
186 throws IOException
187 {
188 return Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
189 }
190
191 /***********************************************************************************************************************************************************
192 *
193 **********************************************************************************************************************************************************/
194 @Nonnull
195 private static ZonedDateTime toZoneDateTime (@Nonnull final FileTime dateTime)
196 {
197 return ZonedDateTime.ofInstant(dateTime.toInstant(), ZoneId.systemDefault());
198 }
199 }