Package: PathAwareEntityFinderDelegate
PathAwareEntityFinderDelegate
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
PathAwareEntityFinderDelegate(MediaFolder, Finder) |
|
|
|
|
|
||||||||||||||||||||
PathAwareEntityFinderDelegate(MediaFolder, Finder, Optional) |
|
|
|
|
|
||||||||||||||||||||
PathAwareEntityFinderDelegate(MediaFolder, Function) |
|
|
|
|
|
||||||||||||||||||||
PathAwareEntityFinderDelegate(PathAwareEntityFinderDelegate, Object) |
|
|
|
|
|
||||||||||||||||||||
asSimpleComposite(As) |
|
|
|
|
|
||||||||||||||||||||
childMatchingPath(PathAwareEntity, Path) |
|
|
|
|
|
||||||||||||||||||||
childMatchingPathHead(Path) |
|
|
|
|
|
||||||||||||||||||||
computeResults() |
|
|
|
|
|
||||||||||||||||||||
count() |
|
|
|
|
|
||||||||||||||||||||
filteredByPath(Path) |
|
|
|
|
|
||||||||||||||||||||
lambda$childMatchingPathHead$7(Path, PathAwareEntity) |
|
|
|
|
|
||||||||||||||||||||
lambda$computeResults$1(PathAwareEntity) |
|
|
|
|
|
||||||||||||||||||||
lambda$computeResults$2(Path) |
|
|
|
|
|
||||||||||||||||||||
lambda$count$3(Path) |
|
|
|
|
|
||||||||||||||||||||
lambda$count$4(PathAwareEntity) |
|
|
|
|
|
||||||||||||||||||||
lambda$count$5(Path) |
|
|
|
|
|
||||||||||||||||||||
lambda$filteredByPath$6(Path, PathAwareEntity) |
|
|
|
|
|
||||||||||||||||||||
lambda$new$0(Function, MediaFolder) |
|
|
|
|
|
||||||||||||||||||||
relative(Path) |
|
|
|
|
|
||||||||||||||||||||
sameHead(Path, Path) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
withPath(Path) |
|
|
|
|
|
Coverage
1: /*
2: * *********************************************************************************************************************
3: *
4: * blueMarine II: Semantic Media Centre
5: * http://tidalwave.it/projects/bluemarine2
6: *
7: * Copyright (C) 2015 - 2021 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
12: * the License. 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
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/bluemarine2-src
23: * git clone https://github.com/tidalwave-it/bluemarine2-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.bluemarine2.model.impl;
28:
29: import javax.annotation.Nonnegative;
30: import javax.annotation.Nonnull;
31: import javax.annotation.Nullable;
32: import java.util.Collection;
33: import java.util.List;
34: import java.util.Optional;
35: import java.util.concurrent.CopyOnWriteArrayList;
36: import java.util.function.Function;
37: import java.nio.file.Path;
38: import it.tidalwave.util.As;
39: import it.tidalwave.util.Finder;
40: import it.tidalwave.util.SupplierBasedFinder;
41: import it.tidalwave.util.spi.FinderSupport;
42: import it.tidalwave.role.SimpleComposite;
43: import it.tidalwave.bluemarine2.model.MediaFolder;
44: import it.tidalwave.bluemarine2.model.spi.PathAwareEntity;
45: import it.tidalwave.bluemarine2.model.spi.PathAwareFinder;
46: import lombok.RequiredArgsConstructor;
47: import lombok.extern.slf4j.Slf4j;
48: import static java.util.Collections.singletonList;
49: import static it.tidalwave.role.SimpleComposite._SimpleComposite_;
50: import static lombok.AccessLevel.PRIVATE;
51:
52: /***********************************************************************************************************************
53: *
54: * A decorator of an {@link Finder} of {@link PathAwareEntity} that creates a virtual tree of entities. Each entity is
55: * given a path, which starts with the path of a {@link MediaFolder} and continues with the id of the entity.
56: *
57: * This {@code Finder} can filtered by path. If a filter path is provided, the filtering happens in memory: this means
58: * that even when the delegate queries a native store, all the data are first retrieved in memory.
59: *
60: * @stereotype Finder
61: *
62: * @author Fabrizio Giudici
63: *
64: **********************************************************************************************************************/
65: @RequiredArgsConstructor(access = PRIVATE) @Slf4j
66: public class PathAwareEntityFinderDelegate extends FinderSupport<PathAwareEntity, PathAwareFinder> implements PathAwareFinder
67: {
68: private static final long serialVersionUID = 4429676480224742813L;
69:
70: @Nonnull
71: private final MediaFolder mediaFolder;
72:
73: @Nonnull
74: private final Finder<PathAwareEntity> delegate;
75:
76: @Nonnull
77: private final Optional<Path> optionalPath;
78:
79: /*******************************************************************************************************************
80: *
81: * Creates an instance associated to a given {@link MediaFolder} and a delegate finder.
82: *
83: * @see #PathAwareEntityFinderDelegate(it.tidalwave.bluemarine2.model.MediaFolder, java.util.function.Function)
84: *
85: * @param mediaFolder the folder associated to this finder
86: * @param delegate the delegate finder to provide data
87: *
88: ******************************************************************************************************************/
89: public PathAwareEntityFinderDelegate (@Nonnull final MediaFolder mediaFolder,
90: @Nonnull final Finder<PathAwareEntity> delegate)
91: {
92: this(mediaFolder, delegate, Optional.empty());
93: }
94:
95: /*******************************************************************************************************************
96: *
97: * Creates an instance associated to a given {@link MediaFolder} and a function for providing children. This
98: * constructor is typically used when the children are already present in memory (e.g. they are
99: * {@link VirtualMediaFolder}s. Because the function doesn't have the full semantics of a {@link Finder} - it can't
100: * optimise a query in function of search parameters, nor optimise the count of results - when a
101: * {@code PathAwareEntityFinderDelegate} is created in this way all operations will be performed in memory. If one
102: * can provide data from a native store and enjoy optimised queries, instead of this constructor use
103: * {@link #PathAwareEntityFinderDelegate(it.tidalwave.bluemarine2.model.MediaFolder, it.tidalwave.util.Finder)}
104: *
105: * @see #PathAwareEntityFinderDelegate(it.tidalwave.bluemarine2.model.MediaFolder, it.tidalwave.util.Finder)
106: *
107: * @param mediaFolder the folder associated to this finder
108: * @param function the function that provides children
109: *
110: ******************************************************************************************************************/
111: public PathAwareEntityFinderDelegate (@Nonnull final MediaFolder mediaFolder,
112: @Nonnull final Function<MediaFolder, Collection<? extends PathAwareEntity>> function)
113: {
114: this(mediaFolder, new SupplierBasedFinder<>(() -> function.apply(mediaFolder)), Optional.empty());
115: }
116:
117: /*******************************************************************************************************************
118: *
119: * Clone constructor.
120: *
121: ******************************************************************************************************************/
122: public PathAwareEntityFinderDelegate (@Nonnull final PathAwareEntityFinderDelegate other,
123: @Nonnull final Object override)
124: {
125: super(other, override);
126: final PathAwareEntityFinderDelegate source = getSource(PathAwareEntityFinderDelegate.class, other, override);
127: this.mediaFolder = source.mediaFolder;
128: this.delegate = source.delegate;
129: this.optionalPath = source.optionalPath;
130: }
131:
132: /*******************************************************************************************************************
133: *
134: * {@inheritDoc}
135: *
136: ******************************************************************************************************************/
137: @Override @Nonnull
138: public PathAwareFinder withPath (@Nonnull final Path path)
139: {
140: return clonedWith(new PathAwareEntityFinderDelegate(mediaFolder, delegate, Optional.of(path)));
141: }
142:
143: /*******************************************************************************************************************
144: *
145: * {@inheritDoc}
146: *
147: ******************************************************************************************************************/
148: @Override @Nonnull
149: protected List<? extends PathAwareEntity> computeResults()
150: {
151: return new CopyOnWriteArrayList<>(optionalPath.flatMap(path -> filteredByPath(path).map(e -> singletonList(e)))
152: .orElse((List)delegate.results()));
153: }
154:
155: /*******************************************************************************************************************
156: *
157: * {@inheritDoc}
158: *
159: ******************************************************************************************************************/
160: @Override @Nonnegative
161: public int count()
162: {
163: optionalPath.ifPresent(path -> log.warn("Path present: {} - count won't be a native query", path));
164: return optionalPath.map(path -> filteredByPath(path).map(entity -> 1).orElse(0))
165: .orElse(delegate.count());
166: }
167:
168: /*******************************************************************************************************************
169: *
170: *
171: *
172: ******************************************************************************************************************/
173: @Nonnull
174: private Optional<? extends PathAwareEntity> filteredByPath (@Nonnull final Path path)
175: {
176: log.debug("filteredByPath({})", path);
177:• return mediaFolder.getPath().equals(path)
178: ? Optional.of(mediaFolder)
179:• : childMatchingPathHead(path).flatMap(entity -> path.equals(entity.getPath())
180: ? Optional.of(entity)
181: : childMatchingPath(entity, path));
182: }
183:
184: /*******************************************************************************************************************
185: *
186: * Returns the child entity that matches the first element of the path, if present. The path can be exactly the one
187: * of the found entity, or it can be of one of its children.
188: *
189: * This method performs a bulk query of all children and then filters by path in memory. It is not possible to
190: * use a query to the native store for the path - which would be good for performance reasons - , because even
191: * though each segment of the path is function of some attribute of the related {@code PathAwareEntity} - typically
192: * the id - it is not a matter of the native store. Performance of this section relies upon memory caching. Some
193: * experiment showed that it's not useful to add another caching layer here, and the one in
194: * {@code RepositoryFinderSupport} is enough.
195: *
196: * @param path the path
197: * @return the entity, if present
198: *
199: ******************************************************************************************************************/
200: @Nonnull
201: private Optional<PathAwareEntity> childMatchingPathHead (@Nonnull final Path path)
202: {
203: // assert filtered.size() == 1 or 0;
204: log.debug(">>>> bulk query to {}, filtering in memory", delegate);
205: return (Optional<PathAwareEntity>)delegate.results().stream()
206: .filter(entity -> sameHead(relative(path), relative(entity.getPath())))
207: .findFirst();
208: }
209:
210: /*******************************************************************************************************************
211: *
212: * @param entity
213: * @param path the path
214: * @return the entity, if present
215: *
216: ******************************************************************************************************************/
217: @Nonnull
218: private static Optional<PathAwareEntity> childMatchingPath (@Nonnull final PathAwareEntity entity,
219: @Nonnull final Path path)
220: {
221: return ((PathAwareFinder)asSimpleComposite(entity).findChildren()).withPath(path).optionalResult();
222: }
223:
224: /*******************************************************************************************************************
225: *
226: *
227: *
228: ******************************************************************************************************************/
229: @Nonnull // FIXME: this should be normally done by as()
230: private static SimpleComposite asSimpleComposite (@Nonnull final As object)
231: {
232:• return (object instanceof SimpleComposite) ? (SimpleComposite)object : object.as(_SimpleComposite_);
233: }
234:
235: /*******************************************************************************************************************
236: *
237: * Relativizes a path against the finder path, that is it removes the parent path. If the path can't be
238: * relativized, that is it doesn't start with the finder path, returns null.
239: *
240: ******************************************************************************************************************/
241: @Nullable
242: private Path relative (@Nonnull final Path path)
243: {
244:• return mediaFolder.getParent().isEmpty() ? path :
245:• path.startsWith(mediaFolder.getPath()) ? path.subpath(mediaFolder.getPath().getNameCount(), path.getNameCount())
246: : null;
247: }
248:
249: /*******************************************************************************************************************
250: *
251: *
252: *
253: ******************************************************************************************************************/
254: private static boolean sameHead (@Nullable final Path path1, @Nullable final Path path2)
255: {
256:• return (path1 != null) && (path2 != null) && path1.getName(0).equals(path2.getName(0));
257: }
258: }