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