1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.resourceloader.layered;
35
36 import static com.google.common.collect.Iterables.*;
37 import static info.magnolia.resourceloader.ResourceOriginChange.Type.*;
38 import static info.magnolia.resourceloader.ResourceOriginChange.resourceChange;
39
40 import info.magnolia.resourceloader.AbstractResourceOrigin;
41 import info.magnolia.resourceloader.Resource;
42 import info.magnolia.resourceloader.ResourceChangeHandler;
43 import info.magnolia.resourceloader.ResourceChangeHandlerRegistration;
44 import info.magnolia.resourceloader.ResourceOrigin;
45 import info.magnolia.resourceloader.ResourceOriginChange;
46 import info.magnolia.resourceloader.ResourceOriginFactory;
47 import info.magnolia.resourceloader.util.Functions;
48
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.Reader;
52 import java.nio.charset.Charset;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.List;
56
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import com.google.auto.factory.AutoFactory;
61 import com.google.common.base.Function;
62 import com.google.common.base.Optional;
63 import com.google.common.base.Predicate;
64 import com.google.common.collect.Iterables;
65 import com.google.common.collect.Lists;
66 import com.google.common.collect.Ordering;
67
68
69
70
71
72
73 @AutoFactory(implementing = ResourceOriginFactory.class)
74 public class LayeredResourceOrigin extends AbstractResourceOrigin<LayeredResource> {
75
76 private static final Logger log = LoggerFactory.getLogger(LayeredResourceOrigin.class);
77
78 private final List<ResourceOrigin> origins;
79
80
81 LayeredResourceOrigin(String name, ResourceOrigin... origins) {
82 super(name);
83 this.origins = Arrays.asList(origins);
84 }
85
86 @Override
87 public LayeredResource getRoot() {
88 final List<Resource> roots = Lists.transform(origins, Functions.getRoot());
89 return newLayeredResource(roots);
90 }
91
92 @Override
93 public ResourceChangeHandlerRegistration registerResourceChangeHandler(final ResourceChangeHandler changeHandler) {
94 final ResourceChangeHandler relayingChangeHandler = new RelayingChangeHandler(changeHandler);
95
96 final List<ResourceChangeHandlerRegistration> registrations = Lists.newArrayListWithCapacity(origins.size() + 1);
97 registrations.add(super.registerResourceChangeHandler(relayingChangeHandler));
98
99 for (final ResourceOrigin origin : origins) {
100 registrations.add(origin.registerResourceChangeHandler(relayingChangeHandler));
101 }
102
103 return new AggregateChangeHandlerRegistration(registrations);
104 }
105
106 @Override
107 public LayeredResource getByPath(String path) {
108 final Iterable<ResourceOrigin> matchingOrigins = filter(this.origins, Functions.hasPath(path));
109 final List<Resource> matchingResources = Lists.newArrayList(transform(matchingOrigins, Functions.getByPath(path)));
110 if (matchingResources.isEmpty()) {
111 throw new ResourceNotFoundException(this, path);
112 }
113 return newLayeredResource(matchingResources);
114 }
115
116 @Override
117 public boolean hasPath(String path) {
118 return any(origins, Functions.hasPath(path));
119 }
120
121 @Override
122 protected boolean isFile(LayeredResource resource) {
123 return resource.getFirst().isFile();
124 }
125
126 @Override
127 protected boolean isDirectory(LayeredResource resource) {
128 return resource.getFirst().isDirectory();
129 }
130
131 @Override
132 protected boolean isEditable(LayeredResource resource) {
133 return resource.getFirst().isEditable();
134 }
135
136 @Override
137 protected String getPath(LayeredResource resource) {
138 return resource.getFirst().getPath();
139 }
140
141 @Override
142 protected String getName(LayeredResource resource) {
143 return resource.getFirst().getName();
144 }
145
146 @Override
147 protected long getLastModified(LayeredResource resource) {
148 return resource.getFirst().getLastModified();
149 }
150
151 @Override
152 protected List<LayeredResource> listChildren(LayeredResource resource) {
153 List<LayeredResource> layeredResources = new ArrayList<>();
154
155
156 final List<Resource> layers = resource.getLayers();
157 for (Resource layer : layers) {
158 List<Resource> layerChildren = layer.listChildren();
159
160
161 if (layeredResources.isEmpty()) {
162 layeredResources.addAll(Lists.transform(layerChildren, new Function<Resource, LayeredResource>() {
163 @Override
164 public LayeredResource apply(Resource input) {
165 return new LayeredResource(LayeredResourceOrigin.this, input.getPath(), Lists.newArrayList(input));
166 }
167 }));
168 continue;
169 }
170
171 for (final Resource r : layerChildren) {
172 Optional<LayeredResource> matchingResource = Iterables.tryFind(layeredResources, new Predicate<LayeredResource>() {
173 @Override
174 public boolean apply(LayeredResource input) {
175 return r.getPath().equals(input.getPath()) && r.isFile() == input.isFile();
176 }
177 });
178 if (matchingResource.isPresent()) {
179 LayeredResource l = matchingResource.get();
180 l.getLayers().add(r);
181 } else {
182 LayeredResource l = new LayeredResource(this, r.getPath(), Lists.newArrayList(r));
183 layeredResources.add(l);
184 }
185 }
186 }
187 return Ordering.from(Functions.compareByHandle()).immutableSortedCopy(layeredResources);
188 }
189
190 @Override
191 protected LayeredResource getParent(LayeredResource resource) {
192
193
194
195
196 Resource parent = resource.getFirst().getParent();
197 return parent != null ? getByPath(parent.getPath()) : null;
198 }
199
200 @Override
201 protected InputStream doOpenStream(LayeredResource resource) throws IOException {
202 return resource.getFirst().openStream();
203 }
204
205 @Override
206 protected Reader openReader(LayeredResource resource) throws IOException {
207 return resource.getFirst().openReader();
208 }
209
210
211
212
213
214
215 @Override
216 protected Charset getCharsetFor(LayeredResource resource) {
217 throw new IllegalStateException("This method should not be called");
218 }
219
220 protected LayeredResource newLayeredResource(List<Resource> resources) {
221
222 final String path = resources.get(0).getPath();
223 final boolean isDirectory = resources.get(0).isDirectory();
224 if (!all(resources, Functions.pathEquals(path))) {
225 throw new IllegalStateException("Given resources don't match path [" + path + "]: " + resources);
226 }
227 final Predicate<Resource> dirOrFilePredicate = isDirectory ? Functions.isDirectory() : Functions.isFile();
228 if (!all(resources, dirOrFilePredicate)) {
229 log.warn("Resources at {} are not all directory/file: {}", path, resources);
230 resources = Lists.newArrayList(Iterables.filter(resources, dirOrFilePredicate));
231 }
232 return new LayeredResource(this, path, resources);
233 }
234
235
236
237
238
239 private static class AggregateChangeHandlerRegistration implements ResourceChangeHandlerRegistration {
240
241 private final List<ResourceChangeHandlerRegistration> registrations;
242
243 public AggregateChangeHandlerRegistration(List<ResourceChangeHandlerRegistration> registrations) {
244 this.registrations = registrations;
245 }
246
247 @Override
248 public void unRegister() {
249 for (final ResourceChangeHandlerRegistration registration : registrations) {
250 registration.unRegister();
251 }
252
253 }
254 }
255
256
257
258
259
260
261
262
263
264
265 private class RelayingChangeHandler implements ResourceChangeHandler {
266
267 private final ResourceChangeHandler delegate;
268
269 RelayingChangeHandler(ResourceChangeHandler delegate) {
270 this.delegate = delegate;
271 }
272
273 @Override
274 public void onResourceChanged(ResourceOriginChange change) {
275 final Optional<LayeredResource> resource = hasPath(change.getRelatedResourcePath()) ? Optional.of(getByPath(change.getRelatedResourcePath())) : Optional.<LayeredResource>absent();
276 final ResourceOriginChange.Builder relayedChange =
277 resourceChange().
278 inOrigin(LayeredResourceOrigin.this).
279 at(change.getRelatedResourcePath());
280
281 switch (change.getType()) {
282 case MODIFIED:
283
284 if (resource.isPresent() && resource.get().getFirst().getOrigin().equals(change.getRelatedOrigin())) {
285 delegate.onResourceChanged(relayedChange.ofType(MODIFIED).build());
286 }
287 break;
288 case ADDED:
289
290 if (resource.isPresent() && resource.get().getFirst().getOrigin().equals(change.getRelatedOrigin())) {
291 delegate.onResourceChanged(relayedChange.ofType(ADDED).build());
292 }
293 break;
294 case REMOVED:
295
296 if (!resource.isPresent()) {
297 delegate.onResourceChanged(relayedChange.ofType(REMOVED).build());
298 } else {
299
300 int changedOriginPriority = origins.indexOf(change.getRelatedOrigin());
301 int currentOriginPriority = origins.indexOf(resource.get().getFirst().getOrigin());
302 if (changedOriginPriority > currentOriginPriority) {
303 delegate.onResourceChanged(relayedChange.ofType(MODIFIED).build());
304 }
305 }
306 break;
307 }
308 }
309 }
310 }