001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.graph.request;
025
026 import org.jboss.dna.common.util.CheckArg;
027 import org.jboss.dna.common.util.HashCode;
028 import org.jboss.dna.graph.GraphI18n;
029 import org.jboss.dna.graph.Location;
030 import org.jboss.dna.graph.NodeConflictBehavior;
031 import org.jboss.dna.graph.property.Name;
032 import org.jboss.dna.graph.property.Path;
033
034 /**
035 * Instruction that a branch be copied from one location into another. This request can copy a branch in one workspace into
036 * another workspace, or it can copy a branch in the same workspace.
037 *
038 * @author Randall Hauch
039 */
040 public class CopyBranchRequest extends ChangeRequest {
041
042 private static final long serialVersionUID = 1L;
043
044 public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
045
046 private final Location from;
047 private final Location into;
048 private final String fromWorkspace;
049 private final String intoWorkspace;
050 private final Name desiredNameForCopy;
051 private final NodeConflictBehavior conflictBehavior;
052 private Location actualFromLocation;
053 private Location actualIntoLocation;
054
055 /**
056 * Create a request to copy a branch to another.
057 *
058 * @param from the location of the top node in the existing branch that is to be copied
059 * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
060 * @param into the location of the existing node into which the copy should be placed
061 * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied, or null if the source's
062 * default workspace is to be used
063 * @throws IllegalArgumentException if any of the parameters are null
064 */
065 public CopyBranchRequest( Location from,
066 String fromWorkspace,
067 Location into,
068 String intoWorkspace ) {
069 this(from, fromWorkspace, into, intoWorkspace, null, DEFAULT_CONFLICT_BEHAVIOR);
070 }
071
072 /**
073 * Create a request to copy a branch to another.
074 *
075 * @param from the location of the top node in the existing branch that is to be copied
076 * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
077 * @param into the location of the existing node into which the copy should be placed
078 * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
079 * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
080 * used
081 * @throws IllegalArgumentException if any of the parameters are null
082 */
083 public CopyBranchRequest( Location from,
084 String fromWorkspace,
085 Location into,
086 String intoWorkspace,
087 Name nameForCopy ) {
088 this(from, fromWorkspace, into, intoWorkspace, nameForCopy, DEFAULT_CONFLICT_BEHAVIOR);
089 }
090
091 /**
092 * Create a request to copy a branch to another.
093 *
094 * @param from the location of the top node in the existing branch that is to be copied
095 * @param fromWorkspace the name of the workspace where the <code>from</code> node exists
096 * @param into the location of the existing node into which the copy should be placed
097 * @param intoWorkspace the name of the workspace where the <code>into</code> node is to be copied
098 * @param nameForCopy the desired name for the node that results from the copy, or null if the name of the original should be
099 * used
100 * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
101 * location
102 * @throws IllegalArgumentException if any of the parameters are null
103 */
104 public CopyBranchRequest( Location from,
105 String fromWorkspace,
106 Location into,
107 String intoWorkspace,
108 Name nameForCopy,
109 NodeConflictBehavior conflictBehavior ) {
110 CheckArg.isNotNull(from, "from");
111 CheckArg.isNotNull(into, "into");
112 CheckArg.isNotNull(fromWorkspace, "fromWorkspace");
113 CheckArg.isNotNull(intoWorkspace, "intoWorkspace");
114 CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
115 this.from = from;
116 this.into = into;
117 this.fromWorkspace = fromWorkspace;
118 this.intoWorkspace = intoWorkspace;
119 this.desiredNameForCopy = nameForCopy;
120 this.conflictBehavior = conflictBehavior;
121 }
122
123 /**
124 * Get the location defining the top of the branch to be copied
125 *
126 * @return the from location; never null
127 */
128 public Location from() {
129 return from;
130 }
131
132 /**
133 * Get the location defining the parent where the new copy is to be placed
134 *
135 * @return the to location; never null
136 */
137 public Location into() {
138 return into;
139 }
140
141 /**
142 * Get the name of the workspace containing the branch to be copied.
143 *
144 * @return the name of the workspace containing the branch to be copied; never null
145 */
146 public String fromWorkspace() {
147 return fromWorkspace;
148 }
149
150 /**
151 * Get the name of the workspace where the copy is to be placed
152 *
153 * @return the name of the workspace where the copy is to be placed; never null
154 */
155 public String intoWorkspace() {
156 return intoWorkspace;
157 }
158
159 /**
160 * Determine whether this copy operation is within the same workspace.
161 *
162 * @return true if this operation is to be performed within the same workspace, or false if the workspace of the
163 * {@link #from() original} is different than that of the {@link #into() copy}
164 */
165 public boolean isSameWorkspace() {
166 return fromWorkspace.equals(intoWorkspace);
167 }
168
169 /**
170 * Get the name of the copy if it is to be different than that of the original.
171 *
172 * @return the desired name of the copy, or null if the name of the original is to be used
173 */
174 public Name desiredName() {
175 return desiredNameForCopy;
176 }
177
178 /**
179 * {@inheritDoc}
180 *
181 * @see org.jboss.dna.graph.request.Request#isReadOnly()
182 */
183 @Override
184 public boolean isReadOnly() {
185 return false;
186 }
187
188 /**
189 * Get the expected behavior when copying the branch and the {@link #into() destination} already has a node with the same
190 * name.
191 *
192 * @return the behavior specification
193 */
194 public NodeConflictBehavior conflictBehavior() {
195 return conflictBehavior;
196 }
197
198 /**
199 * Sets the actual and complete location of the node being renamed and its new location. This method must be called when
200 * processing the request, and the actual location must have a {@link Location#getPath() path}.
201 *
202 * @param fromLocation the actual location of the node being copied
203 * @param intoLocation the actual location of the new copy of the node
204 * @throws IllegalArgumentException if the either location is null; if the old location does not represent the
205 * {@link Location#isSame(Location) same location} as the {@link #from() from location}; if the new location does not
206 * represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}; if the either
207 * location does not have a path
208 * @throws IllegalStateException if the request is frozen
209 */
210 public void setActualLocations( Location fromLocation,
211 Location intoLocation ) {
212 checkNotFrozen();
213 if (!from.isSame(fromLocation)) { // not same if actual is null
214 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(fromLocation, from));
215 }
216 CheckArg.isNotNull(intoLocation, "intoLocation");
217 assert fromLocation != null;
218 assert intoLocation != null;
219 if (!fromLocation.hasPath()) {
220 throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(fromLocation));
221 }
222 if (!intoLocation.hasPath()) {
223 throw new IllegalArgumentException(GraphI18n.actualNewLocationMustHavePath.text(intoLocation));
224 }
225 // The 'into' should be the parent of the 'newLocation' ...
226 if (into.hasPath() && !intoLocation.getPath().getParent().equals(into.getPath())) {
227 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotChildOfInputLocation.text(intoLocation, into));
228 }
229 this.actualFromLocation = fromLocation;
230 this.actualIntoLocation = intoLocation;
231 }
232
233 /**
234 * Get the actual location of the node before being copied.
235 *
236 * @return the actual location of the node before being moved, or null if the actual location was not set
237 */
238 public Location getActualLocationBefore() {
239 return actualFromLocation;
240 }
241
242 /**
243 * Get the actual location of the node after being copied.
244 *
245 * @return the actual location of the node after being copied, or null if the actual location was not set
246 */
247 public Location getActualLocationAfter() {
248 return actualIntoLocation;
249 }
250
251 /**
252 * {@inheritDoc}
253 *
254 * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
255 */
256 @Override
257 public boolean changes( String workspace,
258 Path path ) {
259 return this.intoWorkspace.equals(workspace) && into.hasPath() && into.getPath().isAtOrBelow(path);
260 }
261
262 /**
263 * {@inheritDoc}
264 *
265 * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
266 */
267 @Override
268 public Location changedLocation() {
269 return into;
270 }
271
272 /**
273 * {@inheritDoc}
274 *
275 * @see org.jboss.dna.graph.request.ChangeRequest#changedWorkspace()
276 */
277 @Override
278 public String changedWorkspace() {
279 return intoWorkspace();
280 }
281
282 /**
283 * {@inheritDoc}
284 *
285 * @see java.lang.Object#hashCode()
286 */
287 @Override
288 public int hashCode() {
289 return HashCode.compute(from, fromWorkspace, into, intoWorkspace);
290 }
291
292 /**
293 * {@inheritDoc}
294 *
295 * @see org.jboss.dna.graph.request.Request#cancel()
296 */
297 @Override
298 public void cancel() {
299 super.cancel();
300 this.actualFromLocation = null;
301 this.actualIntoLocation = null;
302 }
303
304 /**
305 * {@inheritDoc}
306 *
307 * @see java.lang.Object#equals(java.lang.Object)
308 */
309 @Override
310 public boolean equals( Object obj ) {
311 if (obj == this) return true;
312 if (this.getClass().isInstance(obj)) {
313 CopyBranchRequest that = (CopyBranchRequest)obj;
314 if (!this.from().equals(that.from())) return false;
315 if (!this.into().equals(that.into())) return false;
316 if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
317 if (!this.fromWorkspace.equals(that.fromWorkspace)) return false;
318 if (!this.intoWorkspace.equals(that.intoWorkspace)) return false;
319 return true;
320 }
321 return false;
322 }
323
324 /**
325 * {@inheritDoc}
326 *
327 * @see java.lang.Object#toString()
328 */
329 @Override
330 public String toString() {
331 if (fromWorkspace.equals(intoWorkspace)) {
332 if (desiredNameForCopy != null) {
333 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into() + " with name "
334 + desiredNameForCopy;
335 }
336 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into();
337 }
338 if (desiredNameForCopy != null) {
339 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into() + " with name "
340 + desiredNameForCopy + " in the \"" + intoWorkspace + "\" workspace";
341 }
342 return "copy branch " + from() + " in the \"" + fromWorkspace + "\" workspace into " + into() + " in the \""
343 + intoWorkspace + "\" workspace";
344 }
345 }