View Javadoc

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.Iterator;
27  import java.util.List;
28  import net.jcip.annotations.Immutable;
29  import org.modeshape.graph.ExecutionContext;
30  import org.modeshape.graph.Location;
31  import org.modeshape.graph.connector.federation.Projection.Rule;
32  import org.modeshape.graph.property.Path;
33  import org.modeshape.graph.property.PathFactory;
34  import org.modeshape.graph.property.Path.Segment;
35  
36  /**
37   * A Projector for federated repository configurations that have a single mirrored projection (a projection that is a one-for-one
38   * mapping to a single source) and a single branch projection that maps a single path in the source to a single path in the
39   * federated repository.
40   * <p>
41   * For example, consider a federated repository that maps the "/" path in source1 into "/" in the federated repository, and the
42   * "/path/in/source" path into the "/federated/path" branch in the federated repository. Thus, anything at or below
43   * "/federated/path" goes into the (If this were the only projection, then a {@link MirrorProjector} could be used.)
44   */
45  @Immutable
46  final class BranchedMirrorProjector extends ProjectorWithPlaceholders {
47  
48      /**
49       * Attempt to create an instance of the {@link BranchedMirrorProjector} with the supplied projections using the supplied
50       * context.
51       * 
52       * @param context the context; may not be null
53       * @param projections the projections in the federated repository; may not be null
54       * @return the branched mirror projector, or null if the projections didn't match the criteria for such a projector
55       */
56      static BranchedMirrorProjector with( ExecutionContext context,
57                                           List<Projection> projections ) {
58          assert projections != null;
59          assert context != null;
60          if (projections.size() != 2) return null;
61          Projection first = projections.get(0);
62          Projection second = projections.get(1);
63          if (first.getRules().size() != 1) return null;
64          if (second.getRules().size() != 1) return null;
65          Rule firstRule = first.getRules().get(0);
66          Rule secondRule = second.getRules().get(0);
67          assert firstRule != null;
68          assert secondRule != null;
69          PathFactory pathFactory = context.getValueFactories().getPathFactory();
70          List<Path> firstTopLevelPaths = first.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
71          if (firstTopLevelPaths.size() != 1) return null;
72          List<Path> secondTopLevelPaths = second.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
73          if (secondTopLevelPaths.size() != 1) return null;
74          Path firstTopLevelPath = firstTopLevelPaths.get(0);
75          Path secondTopLevelPath = secondTopLevelPaths.get(0);
76          if (firstTopLevelPath.isRoot()) {
77              // We're good, so create the instance ...
78              return new BranchedMirrorProjector(context, projections, first, second, secondTopLevelPath,
79                                                 secondRule.getPathInSource(secondTopLevelPath, pathFactory));
80          }
81          // the second top-level path must be a root ...
82          if (!secondTopLevelPath.isRoot()) return null;
83          // We're good, so create the instance ...
84          return new BranchedMirrorProjector(context, projections, second, first, firstTopLevelPath,
85                                             firstRule.getPathInSource(firstTopLevelPath, pathFactory));
86      }
87  
88      private final Projection mirrorProjection;
89      private final Projection branchProjection;
90      private final Path branchFederatedPath;
91      private final Path branchSourcePath;
92      private final boolean branchSourceUsesSamePath;
93  
94      BranchedMirrorProjector( ExecutionContext context,
95                               List<Projection> projections,
96                               Projection mirrorProjection,
97                               Projection branchProjection,
98                               Path branchFederatedPath,
99                               Path branchSourcePath ) {
100         super(context, projections);
101         assert mirrorProjection != null;
102         assert branchProjection != null;
103         assert branchFederatedPath != null;
104         assert branchSourcePath != null;
105         this.mirrorProjection = mirrorProjection;
106         this.branchProjection = branchProjection;
107         this.branchFederatedPath = branchFederatedPath;
108         this.branchSourcePath = branchSourcePath;
109         this.branchSourceUsesSamePath = branchSourcePath.equals(branchFederatedPath);
110     }
111 
112     /**
113      * {@inheritDoc}
114      * <p>
115      * This implementation <i>always<i> returns a single {@link ProxyNode} for the location in the single projection.
116      * </p>
117      * 
118      * @see org.modeshape.graph.connector.federation.Projector#project(ExecutionContext, Location, boolean)
119      */
120     public ProjectedNode project( ExecutionContext context,
121                                   Location location,
122                                   boolean requiresUpdate ) {
123         assert location != null;
124         if (location.hasPath()) {
125             Path path = location.getPath();
126             if (path.isRoot()) {
127                 // It is a projection of the mirror's root and the placeholder for the branch root ...
128                 if (requiresUpdate && mirrorProjection.isReadOnly()) return null;
129                 ProxyNode result = new ProxyNode(mirrorProjection, location, location.with(branchSourcePath),
130                                                  branchSourceUsesSamePath);
131                 result.add(isPlaceholder(location));
132                 return result;
133             }
134             ProjectedNode onBranch = isOnBranch(path, location, context);
135             if (onBranch != null) {
136                 if (requiresUpdate && branchProjection.isReadOnly()) return null;
137                 return onBranch;
138             }
139             // Otherwise it is a proxy to the mirror only ...
140             if (requiresUpdate && mirrorProjection.isReadOnly()) return null;
141             return new ProxyNode(mirrorProjection, location, location, true);
142         }
143 
144         // The location has no path and only identifier properties ...
145         if (requiresUpdate) {
146             if (branchProjection.isReadOnly()) {
147                 // Can't update branch ...
148                 if (mirrorProjection.isReadOnly()) return null;
149                 return new ProxyNode(mirrorProjection, location, location, true); // no paths
150             }
151             if (mirrorProjection.isReadOnly()) {
152                 // Can't update mirror ...
153                 return new ProxyNode(branchProjection, location, location, branchSourceUsesSamePath); // no paths
154             }
155         }
156 
157         // This is just a read, so create one projection for the mirror, and a second one for the branch.
158         ProjectedNode result = new ProxyNode(mirrorProjection, location, location, true);
159         result.add(new ProxyNode(branchProjection, location, location, branchSourceUsesSamePath)); // no paths
160         return result;
161     }
162 
163     protected final ProjectedNode isOnBranch( Path federatedPath,
164                                               Location location,
165                                               ExecutionContext context ) {
166         Iterator<Segment> branchIter = branchFederatedPath.iterator();
167         Iterator<Segment> federIter = federatedPath.iterator();
168         // Look at the first path ...
169         if (branchIter.hasNext() && federIter.hasNext()) {
170             if (!branchIter.next().equals(federIter.next())) return null; // not on branch
171         }
172         // Otherwise, the federated path is on the branch, but how far ...
173         while (branchIter.hasNext() && federIter.hasNext()) {
174             if (!branchIter.next().equals(federIter.next())) {
175                 // Didn't make it all the way along the branch ...
176                 return null;
177             }
178         }
179         if (branchIter.hasNext()) {
180             // Didn't make it all the way down to the top of the branch, but it is a placeholder ...
181             return isPlaceholder(location);
182         }
183         // Otherwise it is within the brach ...
184         Location locationInSource = location;
185         if (!branchSourceUsesSamePath) {
186             // The source uses a different path ...
187             if (federIter.hasNext()) {
188                 Path subpath = federatedPath.subpath(branchFederatedPath.size());
189                 Path sourcePath = context.getValueFactories().getPathFactory().create(branchSourcePath, subpath);
190                 locationInSource = location.with(sourcePath);
191             } else {
192                 locationInSource = location.with(branchSourcePath);
193             }
194         }
195         return new ProxyNode(branchProjection, locationInSource, location, branchSourceUsesSamePath);
196     }
197 }