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