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.graph.GraphI18n;
028 import org.jboss.dna.graph.Location;
029 import org.jboss.dna.graph.NodeConflictBehavior;
030 import org.jboss.dna.graph.property.Name;
031 import org.jboss.dna.graph.property.Path;
032
033 /**
034 * Instruction that a branch be moved from one location into another.
035 *
036 * @author Randall Hauch
037 */
038 public class MoveBranchRequest extends Request implements ChangeRequest {
039
040 private static final long serialVersionUID = 1L;
041
042 public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
043
044 private final Location from;
045 private final Location into;
046 private final String workspaceName;
047 private final Name desiredNameForNode;
048 private final NodeConflictBehavior conflictBehavior;
049 private Location actualOldLocation;
050 private Location actualNewLocation;
051
052 /**
053 * Create a request to move a branch from one location into another.
054 *
055 * @param from the location of the top node in the existing branch that is to be moved
056 * @param into the location of the existing node into which the branch should be moved
057 * @param workspaceName the name of the workspace
058 * @throws IllegalArgumentException if any of the parameters are null
059 */
060 public MoveBranchRequest( Location from,
061 Location into,
062 String workspaceName ) {
063 this(from, into, workspaceName, null, DEFAULT_CONFLICT_BEHAVIOR);
064 }
065
066 /**
067 * Create a request to move a branch from one location into another.
068 *
069 * @param from the location of the top node in the existing branch that is to be moved
070 * @param into the location of the existing node into which the branch should be moved
071 * @param workspaceName the name of the workspace
072 * @param newNameForMovedNode the new name for the node being moved, or null if the name of the original should be used
073 * @throws IllegalArgumentException if any of the parameters are null
074 */
075 public MoveBranchRequest( Location from,
076 Location into,
077 String workspaceName,
078 Name newNameForMovedNode ) {
079 this(from, into, workspaceName, newNameForMovedNode, DEFAULT_CONFLICT_BEHAVIOR);
080 }
081
082 /**
083 * Create a request to move a branch from one location into another.
084 *
085 * @param from the location of the top node in the existing branch that is to be moved
086 * @param into the location of the existing node into which the branch should be moved
087 * @param workspaceName the name of the workspace
088 * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
089 * location
090 * @throws IllegalArgumentException if any of the parameters are null
091 */
092 public MoveBranchRequest( Location from,
093 Location into,
094 String workspaceName,
095 NodeConflictBehavior conflictBehavior ) {
096 this(from, into, workspaceName, null, conflictBehavior);
097 }
098
099 /**
100 * Create a request to move a branch from one location into another.
101 *
102 * @param from the location of the top node in the existing branch that is to be moved
103 * @param into the location of the existing node into which the branch should be moved
104 * @param workspaceName the name of the workspace
105 * @param newNameForMovedNode the new name for the node being moved, or null if the name of the original should be used
106 * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
107 * location
108 * @throws IllegalArgumentException if any of the parameters are null
109 */
110 public MoveBranchRequest( Location from,
111 Location into,
112 String workspaceName,
113 Name newNameForMovedNode,
114 NodeConflictBehavior conflictBehavior ) {
115 CheckArg.isNotNull(from, "from");
116 CheckArg.isNotNull(into, "into");
117 CheckArg.isNotNull(workspaceName, "workspaceName");
118 CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
119 this.from = from;
120 this.into = into;
121 this.workspaceName = workspaceName;
122 this.desiredNameForNode = newNameForMovedNode;
123 this.conflictBehavior = conflictBehavior;
124 }
125
126 /**
127 * Get the location defining the top of the branch to be moved
128 *
129 * @return the from location; never null
130 */
131 public Location from() {
132 return from;
133 }
134
135 /**
136 * Get the location defining the parent where the branch is to be placed
137 *
138 * @return the to location; never null
139 */
140 public Location into() {
141 return into;
142 }
143
144 /**
145 * Get the name of the workspace in which the branch exists.
146 *
147 * @return the name of the workspace containing the branch; never null
148 */
149 public String inWorkspace() {
150 return workspaceName;
151 }
152
153 /**
154 * Get the name of the copy if it is to be different than that of the original.
155 *
156 * @return the desired name of the copy, or null if the name of the original is to be used
157 */
158 public Name desiredName() {
159 return desiredNameForNode;
160 }
161
162 /**
163 * Get the expected behavior when copying the branch and the {@link #into() destination} already has a node with the same
164 * name.
165 *
166 * @return the behavior specification
167 */
168 public NodeConflictBehavior conflictBehavior() {
169 return conflictBehavior;
170 }
171
172 /**
173 * {@inheritDoc}
174 *
175 * @see org.jboss.dna.graph.request.Request#isReadOnly()
176 */
177 @Override
178 public boolean isReadOnly() {
179 return false;
180 }
181
182 /**
183 * Determine whether this move request can be determined to have no effect.
184 * <p>
185 * A move is known to have no effect when all of the following conditions are true:
186 * <ul>
187 * <li>the {@link #into() into} location has a {@link Location#hasPath() path} but no {@link Location#hasIdProperties()
188 * identification properties};</li>
189 * <li>the {@link #from() from} location has a {@link Location#getPath() path}; and</li>
190 * <li>the {@link #from() from} location's {@link Path#getParent() parent} is the same as the {@link #into() into} location's
191 * path.</li>
192 * </ul>
193 * If all of these conditions are not true, this method returns false.
194 * </p>
195 *
196 * @return true if this move request really doesn't change the parent of the node, or false if it cannot be determined
197 */
198 public boolean hasNoEffect() {
199 if (into.hasPath() && into.hasIdProperties() == false && from.hasPath()) {
200 if (!from.getPath().getParent().equals(into.getPath())) return false;
201 if (desiredName() != null && !desiredName().equals(from.getPath().getLastSegment().getName())) return false;
202 return true;
203 }
204 // Can't be determined for certain
205 return false;
206 }
207
208 /**
209 * Sets the actual and complete location of the node being renamed and its new location. This method must be called when
210 * processing the request, and the actual location must have a {@link Location#getPath() path}.
211 *
212 * @param oldLocation the actual location of the node before being moved
213 * @param newLocation the actual new location of the node
214 * @throws IllegalArgumentException if the either location is null, if the old location does not represent the
215 * {@link Location#isSame(Location) same location} as the {@link #from() from location}, if the new location does not
216 * represent the {@link Location#isSame(Location) same location} as the {@link #into() into location}, or if the
217 * either location does not have a path
218 */
219 public void setActualLocations( Location oldLocation,
220 Location newLocation ) {
221 CheckArg.isNotNull(oldLocation, "oldLocation");
222 CheckArg.isNotNull(newLocation, "newLocation");
223 if (!from.isSame(oldLocation)) { // not same if actual is null
224 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(oldLocation, from));
225 }
226 if (!oldLocation.hasPath()) {
227 throw new IllegalArgumentException(GraphI18n.actualOldLocationMustHavePath.text(oldLocation));
228 }
229 if (!newLocation.hasPath()) {
230 throw new IllegalArgumentException(GraphI18n.actualNewLocationMustHavePath.text(newLocation));
231 }
232 if (into().hasPath() && !newLocation.getPath().getParent().isSameAs(into.getPath())) {
233 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, into));
234 }
235 Name actualNewName = newLocation.getPath().getLastSegment().getName();
236 Name expectedNewName = desiredName() != null ? desiredName() : oldLocation.getPath().getLastSegment().getName();
237 if (!actualNewName.equals(expectedNewName)) {
238 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(newLocation, into));
239 }
240 this.actualOldLocation = oldLocation;
241 this.actualNewLocation = newLocation;
242 }
243
244 /**
245 * Get the actual location of the node before being moved.
246 *
247 * @return the actual location of the node before being moved, or null if the actual location was not set
248 */
249 public Location getActualLocationBefore() {
250 return actualOldLocation;
251 }
252
253 /**
254 * Get the actual location of the node after being moved.
255 *
256 * @return the actual location of the node after being moved, or null if the actual location was not set
257 */
258 public Location getActualLocationAfter() {
259 return actualNewLocation;
260 }
261
262 /**
263 * {@inheritDoc}
264 *
265 * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path)
266 */
267 public boolean changes( String workspace,
268 Path path ) {
269 return this.workspaceName.equals(workspace)
270 && (into.hasPath() && into.getPath().isAtOrBelow(path) || from.hasPath() && from.getPath().isAtOrBelow(path));
271 }
272
273 /**
274 * {@inheritDoc}
275 *
276 * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation()
277 */
278 public Location changedLocation() {
279 return into;
280 }
281
282 /**
283 * {@inheritDoc}
284 *
285 * @see java.lang.Object#equals(java.lang.Object)
286 */
287 @Override
288 public boolean equals( Object obj ) {
289 if (obj == this) return true;
290 if (this.getClass().isInstance(obj)) {
291 MoveBranchRequest that = (MoveBranchRequest)obj;
292 if (!this.from().equals(that.from())) return false;
293 if (!this.into().equals(that.into())) return false;
294 if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
295 if (!this.workspaceName.equals(that.workspaceName)) return false;
296 return true;
297 }
298 return false;
299 }
300
301 /**
302 * {@inheritDoc}
303 *
304 * @see java.lang.Object#toString()
305 */
306 @Override
307 public String toString() {
308 if (desiredName() != null) {
309 return "move branch " + from() + " in the \"" + inWorkspace() + "\" workspace into " + into() + " with name "
310 + desiredName();
311 }
312 return "move branch " + from() + " in the \"" + inWorkspace() + "\" workspace into " + into();
313 }
314 }