Skip to contentMethod: isDirectory()
1: /*
2: * *************************************************************************************************************************************************************
3: *
4: * blueMarine III: Semantic DAM
5: * http://tidalwave.it/projects/bluemarine3
6: *
7: * Copyright (C) 2024 - 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/bluemarine3-src
22: * git clone https://github.com/tidalwave-it/bluemarine3-src
23: *
24: * *************************************************************************************************************************************************************
25: */
26: package it.tidalwave.dam.model.io.impl;
27:
28: import jakarta.annotation.Nonnull;
29: import java.time.ZoneId;
30: import java.time.ZonedDateTime;
31: import java.util.List;
32: import java.util.Objects;
33: import java.util.Optional;
34: import java.util.function.Predicate;
35: import java.util.stream.StreamSupport;
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 java.nio.file.spi.FileSystemProvider;
44: import it.tidalwave.dam.model.MimeType;
45: import it.tidalwave.dam.model.MimeTypeCapable;
46: import it.tidalwave.dam.model.io.FileEntity;
47: import it.tidalwave.dam.model.spi.EntitySupport;
48: import it.tidalwave.util.spi.SimpleFinderSupport;
49: import it.tidalwave.role.SimpleComposite;
50: import lombok.EqualsAndHashCode;
51: import lombok.Getter;
52: import lombok.RequiredArgsConstructor;
53: import lombok.ToString;
54: import lombok.extern.slf4j.Slf4j;
55: import static it.tidalwave.dam.model.MimeTypeCapable._MimeTypeCapable_;
56: import static java.util.Collections.emptyList;
57: import static java.util.Comparator.comparing;
58: import static lombok.AccessLevel.PRIVATE;
59:
60: /***************************************************************************************************************************************************************
61: *
62: * The default implementation of {@link FileEntity}.
63: *
64: * @author Fabrizio Giudici
65: *
66: **************************************************************************************************************************************************************/
67: // @Immutable FIXME
68: @Getter @EqualsAndHashCode(of="path", callSuper = false) @ToString(of = "path") @Slf4j
69: public class DefaultFileEntity extends EntitySupport implements FileEntity
70: {
71: @RequiredArgsConstructor(access = PRIVATE) @SuppressWarnings("serial")
72: private static class ChildrenFinder extends SimpleFinderSupport<FileEntity>
73: {
74: @Nonnull
75: private final FileSystemProvider fsProvider;
76:
77: @Nonnull
78: private final Path path;
79:
80: @Nonnull
81: private final Predicate<FileEntity> filter;
82:
83: /** {@link SimpleFinderSupport}'s special constructor. */
84: public ChildrenFinder (@Nonnull final ChildrenFinder other, @Nonnull final Object override)
85: {
86: super(other, override);
87: final var source = getSource(ChildrenFinder.class, other, override);
88: this.fsProvider = source.fsProvider;
89: this.path = source.path;
90: this.filter = source.filter;
91: }
92:
93: @Override @Nonnull
94: protected List<FileEntity> computeNeededResults()
95: {
96: try (final var dirStream = fsProvider.newDirectoryStream(path, p -> true);
97: final var stream = StreamSupport.stream(dirStream.spliterator(), false))
98: {
99: return stream.map(p -> FileEntity.of(p, filter)).filter(filter).sorted(comparing(f -> f.getPath().toString())).toList();
100: }
101: catch (IOException e)
102: {
103: log.error("While listing directory " + path, e);
104: throw new UncheckedIOException(e);
105: }
106: }
107: }
108:
109: @Nonnull
110: private final FileSystemProvider fileSystemProvider;
111:
112: /** The path of the file.*/
113: @Nonnull
114: private final Path path;
115:
116: /***********************************************************************************************************************************************************
117: * Creates a new instance related to the given path.
118: * @param path the path
119: * @param filter a predicate to filter children
120: **********************************************************************************************************************************************************/
121: public DefaultFileEntity (@Nonnull final Path path, @Nonnull final Predicate<FileEntity> filter)
122: {
123: this(path, filter, path.getFileSystem().provider());
124: }
125:
126: /***********************************************************************************************************************************************************
127: * Creates a new instance related to the given path. This variant is for testing.
128: * @param path the path
129: * @param filter a predicate to filter children
130: * @param fsProvider the file system provider
131: **********************************************************************************************************************************************************/
132: protected DefaultFileEntity (@Nonnull final Path path, @Nonnull final Predicate<FileEntity> filter, @Nonnull final FileSystemProvider fsProvider)
133: {
134: super(Files.isDirectory(path) ? List.of(SimpleComposite.of(new ChildrenFinder(fsProvider, path, filter))) : emptyList());
135: this.fileSystemProvider = fsProvider;
136: this.path = path;
137: }
138:
139: /***********************************************************************************************************************************************************
140: * {@inheritDoc}
141: **********************************************************************************************************************************************************/
142: @Override @Nonnull
143: public String getDisplayName()
144: {
145: return Optional.ofNullable(path.getFileName()).map(Path::toString).orElse("/");
146: }
147:
148: /***********************************************************************************************************************************************************
149: * {@inheritDoc}
150: **********************************************************************************************************************************************************/
151: @Override @Nonnull
152: public ZonedDateTime getCreationDateTime()
153: throws IOException
154: {
155: return toZoneDateTime(getBasicFileAttributes().creationTime());
156: }
157:
158: /***********************************************************************************************************************************************************
159: * {@inheritDoc}
160: **********************************************************************************************************************************************************/
161: @Override @Nonnull
162: public ZonedDateTime getLastAccessDateTime()
163: throws IOException
164: {
165: return toZoneDateTime(getBasicFileAttributes().lastAccessTime());
166: }
167:
168: /***********************************************************************************************************************************************************
169: * {@inheritDoc}
170: **********************************************************************************************************************************************************/
171: @Override @Nonnull
172: public ZonedDateTime getLastModifiedDateTime()
173: throws IOException
174: {
175: return toZoneDateTime(getBasicFileAttributes().lastModifiedTime());
176: }
177:
178: /***********************************************************************************************************************************************************
179: * {@inheritDoc}
180: **********************************************************************************************************************************************************/
181: @Override // FIXME @Nonnegative
182: public long getSize()
183: throws IOException
184: {
185: return Files.size(path);
186: }
187:
188: /***********************************************************************************************************************************************************
189: * {@inheritDoc}
190: **********************************************************************************************************************************************************/
191: @Override
192: public boolean isDirectory()
193: {
194: return Files.isDirectory(path);
195: }
196:
197: /***********************************************************************************************************************************************************
198: * {@inheritDoc}
199: **********************************************************************************************************************************************************/
200: @Override
201: public boolean isUnixHiddenFile()
202: {
203: return Objects.requireNonNull(path.getFileName()).toString().startsWith(".");
204: }
205:
206: /***********************************************************************************************************************************************************
207: * {@inheritDoc}
208: **********************************************************************************************************************************************************/
209: @Override @Nonnull
210: public Optional<MimeType> getMimeType()
211: {
212: return maybeAs(_MimeTypeCapable_).map(MimeTypeCapable::getMimeType);
213: }
214:
215: /***********************************************************************************************************************************************************
216: * {@return the basic attributes} of the file.
217: * @throws IOException if the file does not exist or cannot be accessed
218: **********************************************************************************************************************************************************/
219: @Nonnull
220: private BasicFileAttributes getBasicFileAttributes()
221: throws IOException
222: {
223: return Files.getFileAttributeView(path, BasicFileAttributeView.class).readAttributes();
224: }
225:
226: /***********************************************************************************************************************************************************
227: *
228: **********************************************************************************************************************************************************/
229: @Nonnull
230: private static ZonedDateTime toZoneDateTime (@Nonnull final FileTime dateTime)
231: {
232: return ZonedDateTime.ofInstant(dateTime.toInstant(), ZoneId.systemDefault());
233: }
234: }