View Javadoc
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   }