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.request;
25
26 import org.modeshape.common.util.CheckArg;
27 import org.modeshape.common.util.HashCode;
28 import org.modeshape.graph.GraphI18n;
29 import org.modeshape.graph.Location;
30 import org.modeshape.graph.NodeConflictBehavior;
31 import org.modeshape.graph.property.Name;
32 import org.modeshape.graph.property.Path;
33
34 /**
35 * Instruction that a branch be copied from one location into another. This request can copy a branch in one workspace into
36 * another workspace, or it can copy a branch in the same workspace. Copying a branch always results in the generation of new
37 * UUIDs for the copied nodes. {@link CloneBranchRequest Cloning a branch} provides functionality similar to copy, but with the
38 * ability to preserve UUIDs in the move.
39 *
40 * @see CloneBranchRequest
41 */
42 public class CopyBranchRequest extends ChangeRequest {
43
44 private static final long serialVersionUID = 1L;
45
46 public static final NodeConflictBehavior DEFAULT_NODE_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
47
48 private final Location from;
49 private final Location into;
50 private final String fromWorkspace;
51 private final String intoWorkspace;
52 private final Name desiredNameForCopy;
53 private final NodeConflictBehavior nodeConflictBehavior;
54 private Location actualFromLocation;
55 private Location actualIntoLocation;
56
57 /**
58 * Create a request to copy a branch to another.
59 *
60 * @param from the location of the top node in the existing branch that is to be copied
61 * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
62 * @param into the location of the existing node into which the copy should be placed
63 * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied, or null if the source's
64 * default workspace is to be used
65 * @throws IllegalArgumentException if any of the parameters are null
66 */
67 public CopyBranchRequest( Location from,
68 String fromWorkspace,
69 Location into,
70 String intoWorkspace ) {
71 this(from, fromWorkspace, into, intoWorkspace, null, DEFAULT_NODE_CONFLICT_BEHAVIOR);
72 }
73
74 /**
75 * Create a request to copy a branch to another.
76 *
77 * @param from the location of the top node in the existing branch that is to be copied
78 * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
79 * @param into the location of the existing node into which the copy should be placed
80 * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
81 * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
82 * used
83 * @throws IllegalArgumentException if any of the parameters are null
84 */
85 public CopyBranchRequest( Location from,
86 String fromWorkspace,
87 Location into,
88 String intoWorkspace,
89 Name nameForCopy ) {
90 this(from, fromWorkspace, into, intoWorkspace, nameForCopy, DEFAULT_NODE_CONFLICT_BEHAVIOR);
91 }
92
93 /**
94 * Create a request to copy a branch to another.
95 *
96 * @param from the location of the top node in the existing branch that is to be copied
97 * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
98 * @param into the location of the existing node into which the copy should be placed
99 * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
100 * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
101 * used
102 * @param nodeConflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
103 * location
104 * @throws IllegalArgumentException if any of the parameters are null
105 */
106 public CopyBranchRequest( Location from,
107 String fromWorkspace,
108 Location into,
109 String intoWorkspace,
110 Name nameForCopy,
111 NodeConflictBehavior nodeConflictBehavior ) {
112 CheckArg.isNotNull(from, "from");
113 CheckArg.isNotNull(into, "into");
114 CheckArg.isNotNull(fromWorkspace, "fromWorkspace");
115 CheckArg.isNotNull(intoWorkspace, "intoWorkspace");
116 CheckArg.isNotNull(nodeConflictBehavior, "nodeConflictBehavior");
117 this.from = from;
118 this.into = into;
119 this.fromWorkspace = fromWorkspace;
120 this.intoWorkspace = intoWorkspace;
121 this.desiredNameForCopy = nameForCopy;
122 this.nodeConflictBehavior = nodeConflictBehavior;
123 }
124
125 /**
126 * Get the location defining the top of the branch to be copied
127 *
128 * @return the from location; never null
129 */
130 public Location from() {
131 return from;
132 }
133
134 /**
135 * Get the location defining the parent where the new copy is to be placed
136 *
137 * @return the to location; never null
138 */
139 public Location into() {
140 return into;
141 }
142
143 /**
144 * Get the name of the workspace containing the branch to be copied.
145 *
146 * @return the name of the workspace containing the branch to be copied; never null
147 */
148 public String fromWorkspace() {
149 return fromWorkspace;
150 }
151
152 /**
153 * Get the name of the workspace where the copy is to be placed
154 *
155 * @return the name of the workspace where the copy is to be placed; never null
156 */
157 public String intoWorkspace() {
158 return intoWorkspace;
159 }
160
161 /**
162 * Determine whether this copy operation is within the same workspace.
163 *
164 * @return true if this operation is to be performed within the same workspace, or false if the workspace of the
165 * {@link #from() original} is different than that of the {@link #into() copy}
166 */
167 public boolean isSameWorkspace() {
168 return fromWorkspace.equals(intoWorkspace);
169 }
170
171 /**
172 * Get the name of the copy if it is to be different than that of the original.
173 *
174 * @return the desired name of the copy, or null if the name of the original is to be used
175 */
176 public Name desiredName() {
177 return desiredNameForCopy;
178 }
179
180 /**
181 * {@inheritDoc}
182 *
183 * @see org.modeshape.graph.request.Request#isReadOnly()
184 */
185 @Override
186 public boolean isReadOnly() {
187 return false;
188 }
189
190 /**
191 * Get the expected behavior when copying the branch and the {@link #into() destination} already has a node with the same
192 * name.
193 *
194 * @return the behavior specification
195 */
196 public NodeConflictBehavior nodeConflictBehavior() {
197 return nodeConflictBehavior;
198 }
199
200 /**
201 * Sets the actual and complete location of the node being renamed and its new location. This method must be called when
202 * processing the request, and the actual location must have a {@link Location#getPath() path}.
203 *
204 * @param fromLocation the actual location of the node being copied
205 * @param intoLocation the actual location of the new copy of the node
206 * @throws IllegalArgumentException if the either location is null; or if the either location does not have a path
207 * @throws IllegalStateException if the request is frozen
208 */
209 public void setActualLocations( Location fromLocation,
210 Location intoLocation ) {
211 checkNotFrozen();
212 CheckArg.isNotNull(fromLocation, "intoLocation");
213 CheckArg.isNotNull(intoLocation, "intoLocation");
214 if (!fromLocation.hasPath()) {
215 throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(fromLocation));
216 }
217 if (!intoLocation.hasPath()) {
218 throw new IllegalArgumentException(GraphI18n.actualNewLocationMustHavePath.text(intoLocation));
219 }
220 this.actualFromLocation = fromLocation;
221 this.actualIntoLocation = intoLocation;
222 }
223
224 /**
225 * Get the actual location of the node before being copied.
226 *
227 * @return the actual location of the node before being moved, or null if the actual location was not set
228 */
229 public Location getActualLocationBefore() {
230 return actualFromLocation;
231 }
232
233 /**
234 * Get the actual location of the node after being copied.
235 *
236 * @return the actual location of the node after being copied, or null if the actual location was not set
237 */
238 public Location getActualLocationAfter() {
239 return actualIntoLocation;
240 }
241
242 /**
243 * {@inheritDoc}
244 *
245 * @see org.modeshape.graph.request.ChangeRequest#changes(java.lang.String, org.modeshape.graph.property.Path)
246 */
247 @Override
248 public boolean changes( String workspace,
249 Path path ) {
250 return this.intoWorkspace.equals(workspace) && into.hasPath() && into.getPath().isAtOrBelow(path);
251 }
252
253 /**
254 * {@inheritDoc}
255 *
256 * @see org.modeshape.graph.request.ChangeRequest#changedLocation()
257 */
258 @Override
259 public Location changedLocation() {
260 return actualIntoLocation != null ? actualIntoLocation : into;
261 }
262
263 /**
264 * {@inheritDoc}
265 *
266 * @see org.modeshape.graph.request.ChangeRequest#changedWorkspace()
267 */
268 @Override
269 public String changedWorkspace() {
270 return intoWorkspace();
271 }
272
273 /**
274 * {@inheritDoc}
275 *
276 * @see java.lang.Object#hashCode()
277 */
278 @Override
279 public int hashCode() {
280 return HashCode.compute(from, fromWorkspace, into, intoWorkspace);
281 }
282
283 /**
284 * {@inheritDoc}
285 *
286 * @see org.modeshape.graph.request.Request#cancel()
287 */
288 @Override
289 public void cancel() {
290 super.cancel();
291 this.actualFromLocation = null;
292 this.actualIntoLocation = null;
293 }
294
295 /**
296 * {@inheritDoc}
297 *
298 * @see java.lang.Object#equals(java.lang.Object)
299 */
300 @Override
301 public boolean equals( Object obj ) {
302 if (obj == this) return true;
303 if (this.getClass().isInstance(obj)) {
304 CopyBranchRequest that = (CopyBranchRequest)obj;
305 if (!this.from().isSame(that.from())) return false;
306 if (!this.into().isSame(that.into())) return false;
307 if (!this.nodeConflictBehavior().equals(that.nodeConflictBehavior())) return false;
308 if (!this.fromWorkspace.equals(that.fromWorkspace)) return false;
309 if (!this.intoWorkspace.equals(that.intoWorkspace)) return false;
310 return true;
311 }
312 return false;
313 }
314
315 /**
316 * {@inheritDoc}
317 *
318 * @see java.lang.Object#toString()
319 */
320 @Override
321 public String toString() {
322 if (fromWorkspace.equals(intoWorkspace)) {
323 if (desiredNameForCopy != null) {
324 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into() + " with name "
325 + desiredNameForCopy;
326 }
327 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into();
328 }
329 if (desiredNameForCopy != null) {
330 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into() + " with name "
331 + desiredNameForCopy + " in the \"" + intoWorkspace + "\" workspace";
332 }
333 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into() + " in the \""
334 + intoWorkspace + "\" workspace";
335 }
336
337 /**
338 * {@inheritDoc}
339 * <p>
340 * This method does not clone the results.
341 * </p>
342 *
343 * @see org.modeshape.graph.request.ChangeRequest#clone()
344 */
345 @Override
346 public CopyBranchRequest clone() {
347 CopyBranchRequest result = new CopyBranchRequest(actualFromLocation != null ? actualFromLocation : from, fromWorkspace,
348 actualIntoLocation != null ? actualIntoLocation : into, intoWorkspace,
349 desiredNameForCopy, nodeConflictBehavior);
350 result.setActualLocations(actualFromLocation, actualIntoLocation);
351 return result;
352 }
353
354 @Override
355 public RequestType getType() {
356 return RequestType.COPY_BRANCH;
357 }
358 }