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