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 }