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
38 import info.magnolia.resourceloader.AbstractResourceOrigin;
39 import info.magnolia.resourceloader.Resource;
40 import info.magnolia.resourceloader.ResourceOrigin;
41 import info.magnolia.resourceloader.ResourceOriginFactory;
42 import info.magnolia.resourceloader.ResourceVisitor;
43 import info.magnolia.resourceloader.util.Functions;
44
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.io.Reader;
48 import java.nio.charset.Charset;
49 import java.util.Arrays;
50 import java.util.Collection;
51 import java.util.List;
52 import java.util.SortedSet;
53
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import com.google.auto.factory.AutoFactory;
58 import com.google.common.base.Function;
59 import com.google.common.base.Predicate;
60 import com.google.common.collect.ImmutableList;
61 import com.google.common.collect.ImmutableListMultimap;
62 import com.google.common.collect.ImmutableSortedSet;
63 import com.google.common.collect.Iterables;
64 import com.google.common.collect.Lists;
65 import com.google.common.collect.Maps;
66 import com.google.common.collect.Multimaps;
67 import com.google.common.collect.SetMultimap;
68
69
70
71
72
73
74 @AutoFactory(implementing = ResourceOriginFactory.class)
75 public class LayeredResourceOrigin extends AbstractResourceOrigin<LayeredResource> {
76
77 private static final Logger log = LoggerFactory.getLogger(LayeredResourceOrigin.class);
78
79 private final List<ResourceOrigin> origins;
80
81
82 LayeredResourceOrigin(String name, ResourceOrigin... origins) {
83 super(name);
84 this.origins = Arrays.asList(origins);
85 }
86
87 @Override
88 public LayeredResource getRoot() {
89 final List<Resource> roots = Lists.transform(origins, Functions.getRoot());
90 return newLayeredResource(roots);
91 }
92
93 @Override
94 public void watchForChanges(ResourceVisitor visitor) {
95 for (ResourceOrigin origin : origins) {
96 origin.watchForChanges(new RelayerResourceVisitor(this, visitor));
97 }
98 }
99
100 @Override
101 public LayeredResource getByPath(String path) {
102 final Iterable<ResourceOrigin> matchingOrigins = filter(this.origins, Functions.hasPath(path));
103 final List<Resource> matchingResources = Lists.newArrayList(transform(matchingOrigins, Functions.getByPath(path)));
104 if (matchingResources.isEmpty()) {
105 throw new ResourceNotFoundException(this, path);
106 }
107 return newLayeredResource(matchingResources);
108 }
109
110 @Override
111 public boolean hasPath(String path) {
112 return any(origins, Functions.hasPath(path));
113 }
114
115 @Override
116 protected boolean isFile(LayeredResource resource) {
117 return resource.getFirst().isFile();
118 }
119
120 @Override
121 protected boolean isDirectory(LayeredResource resource) {
122 return resource.getFirst().isDirectory();
123 }
124
125 @Override
126 protected boolean isEditable(LayeredResource resource) {
127 return resource.getFirst().isEditable();
128 }
129
130 @Override
131 protected String getPath(LayeredResource resource) {
132 return resource.getFirst().getPath();
133 }
134
135 @Override
136 protected String getName(LayeredResource resource) {
137 return resource.getFirst().getName();
138 }
139
140 @Override
141 protected long getLastModified(LayeredResource resource) {
142 return resource.getFirst().getLastModified();
143 }
144
145 @Override
146 protected List<LayeredResource> listChildren(LayeredResource resource) {
147 final Function<Resource, Collection<Resource>> delegatedFunction = new Function<Resource, Collection<Resource>>() {
148 @Override
149 public Collection<Resource> apply(Resource layer) {
150 return layer.listChildren();
151 }
152 };
153 final List<Resource> layers = resource.getLayers();
154 return Lists.newArrayList(aggregateSet(layers, delegatedFunction));
155 }
156
157 @Override
158 protected LayeredResource getParent(LayeredResource resource) {
159
160
161
162
163 Resource parent = resource.getFirst().getParent();
164 return parent != null ? getByPath(parent.getPath()) : null;
165 }
166
167 @Override
168 protected InputStream doOpenStream(LayeredResource resource) throws IOException {
169 return resource.getFirst().openStream();
170 }
171
172 @Override
173 protected Reader openReader(LayeredResource resource) throws IOException {
174 return resource.getFirst().openReader();
175 }
176
177
178
179
180
181
182 @Override
183 protected Charset getCharsetFor(LayeredResource resource) {
184 throw new IllegalStateException("This method should not be called");
185 }
186
187
188
189
190 protected <S> SortedSet<LayeredResource> aggregateSet(List<S> sources, Function<S, Collection<Resource>> delegatedFunction) {
191
192 final ImmutableListMultimap.Builder<String, Resource> builder = ImmutableListMultimap.builder();
193
194
195 for (S source : sources) {
196 final Collection<Resource> result = delegatedFunction.apply(source);
197
198
199
200
201 final SetMultimap<String, Resource> map = Multimaps.forMap(Maps.uniqueIndex(result, Functions.getPath()));
202 builder.putAll(map);
203 }
204 final ImmutableListMultimap<String, Resource> map = builder.build();
205
206
207 final ImmutableSortedSet.Builder<LayeredResource> listBuilder = ImmutableSortedSet.orderedBy(Functions.<LayeredResource>compareByHandle());
208
209 for (String path : map.keySet()) {
210 final ImmutableList<Resource> resources = map.get(path);
211 listBuilder.add(newLayeredResource(resources));
212 }
213 return listBuilder.build();
214 }
215
216 protected LayeredResource newLayeredResource(List<Resource> resources) {
217
218 final String path = resources.get(0).getPath();
219 final boolean isDirectory = resources.get(0).isDirectory();
220 if (!all(resources, Functions.pathEquals(path))) {
221 throw new IllegalStateException("Given resources don't match path [" + path + "]: " + resources);
222 }
223 final Predicate<Resource> dirOrFilePredicate = isDirectory ? Functions.isDirectory() : Functions.isFile();
224 if (!all(resources, dirOrFilePredicate)) {
225 log.warn("Resources at {} are not all directory/file: {}", path, resources);
226 resources = Lists.newArrayList(Iterables.filter(resources, dirOrFilePredicate));
227 }
228 return new LayeredResource(this, path, resources);
229 }
230
231 }