1 /*
2 * ModeShape (http://www.modeshape.org)
3 * See the COPYRIGHT.txt file distributed with this work for information
4 * regarding copyright ownership. Some portions may be licensed
5 * to Red Hat, Inc. under one or more contributor license agreements.
6 * See the AUTHORS.txt file in the distribution for a full listing of
7 * individual contributors.
8 *
9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10 * is licensed to you under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * ModeShape is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this software; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23 */
24 package org.modeshape.graph.connector.federation;
25
26 import java.util.List;
27 import java.util.Set;
28 import net.jcip.annotations.Immutable;
29 import org.modeshape.graph.ExecutionContext;
30 import org.modeshape.graph.Location;
31 import org.modeshape.graph.property.Path;
32 import org.modeshape.graph.property.PathFactory;
33
34 /**
35 * A Projector for federated repository configurations that are an offset, direct one-for-one mirror against a single source
36 * repository that is projected below the federated root. In other words, the federated repository has a single projection with a
37 * single "/something/below/root => /" rule.
38 */
39 @Immutable
40 final class GeneralProjector extends ProjectorWithPlaceholders {
41
42 /**
43 * Attempt to create an instance of the {@link GeneralProjector} with the supplied projections using the supplied context.
44 *
45 * @param context the context; may not be null
46 * @param projections the projections in the federated repository; may not be null
47 * @return the offset mirror projector, or null if the projections didn't match the criteria for such a projector
48 */
49 static GeneralProjector with( ExecutionContext context,
50 List<Projection> projections ) {
51 assert projections != null;
52 assert context != null;
53 return new GeneralProjector(context, projections);
54 }
55
56 private final List<Projection> projections;
57
58 private GeneralProjector( ExecutionContext context,
59 List<Projection> projections ) {
60 super(context, projections);
61 this.projections = projections;
62 }
63
64 /**
65 * {@inheritDoc}
66 *
67 * @see org.modeshape.graph.connector.federation.Projector#project(ExecutionContext, Location, boolean)
68 */
69 public ProjectedNode project( ExecutionContext context,
70 Location location,
71 boolean requiresUpdate ) {
72 PlaceholderNode placeholder = isPlaceholder(location);
73 if (placeholder != null) return placeholder;
74
75 // Find the location of the desired node ...
76 Path path = location.getPath();
77 if (path == null) {
78 // There are only identification properties, so return a ProxyNode for each source/workspace ...
79 ProjectedNode result = null;
80 for (Projection projection : projections) {
81 if (requiresUpdate && projection.isReadOnly()) continue;
82 ProxyNode proxy = new ProxyNode(projection, location, location, false);
83 if (result == null) {
84 result = proxy;
85 } else {
86 result.add(proxy);
87 }
88 }
89 return result;
90 }
91 // We have a path, so see if the path is one of the existing placeholder nodes ...
92 ProjectedNode result = isPlaceholder(location);
93 if (result != null) return result;
94
95 // Otherwise this node lives below one (or more) of the projections, and we figure this out each time ...
96 final PathFactory pathFactory = context.getValueFactories().getPathFactory();
97 for (Projection projection : projections) {
98 if (requiresUpdate && projection.isReadOnly()) continue;
99 // Project the federated repository path into the paths as they would exist in the source ...
100 Set<Path> pathsInSource = projection.getPathsInSource(path, pathFactory);
101 for (Path pathInSource : pathsInSource) {
102 // Create a ProxyNode for this projection ...
103 Location locationInSource = Location.create(pathInSource);
104 boolean samePath = pathInSource.equals(path);
105 ProxyNode proxy = new ProxyNode(projection, locationInSource, location, samePath);
106 if (result == null) {
107 result = proxy;
108 } else {
109 result.add(proxy);
110 }
111 }
112 }
113
114 // If there is only one projection ...
115 if (result != null && !result.hasNext() && result.isProxy()) {
116 // Then we want to include the supplied location's ID properties down to the source ...
117 ProxyNode proxy = result.asProxy();
118 result = new ProxyNode(proxy.projection(), location.with(result.location().getPath()), location,
119 proxy.isSameLocationAsOriginal());
120 }
121 return result;
122 }
123
124 }