1 /* 2 * ************************************************************************************************************************************************************* 3 * 4 * SteelBlue: DCI User Interfaces 5 * http://tidalwave.it/projects/steelblue 6 * 7 * Copyright (C) 2015 - 2025 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.ui.example.model; 27 28 import javax.annotation.Nonnegative; 29 import jakarta.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.ui.core.role.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 }