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 java.util.Collections; 027 import java.util.Map; 028 import org.jboss.dna.common.util.CheckArg; 029 import org.jboss.dna.common.util.HashCode; 030 import org.jboss.dna.graph.GraphI18n; 031 import org.jboss.dna.graph.Location; 032 import org.jboss.dna.graph.property.Name; 033 import org.jboss.dna.graph.property.Path; 034 import org.jboss.dna.graph.property.Property; 035 036 /** 037 * Instruction to update the properties on the node at the specified location. 038 * <p> 039 * This request is capable of specifying that certain properties are to have new values and that other properties are to be 040 * removed. The request has a single map of properties keyed by their name. If a property is to be set with new values, the map 041 * will contain an entry with the property keyed by its name. However, if a property is to be removed, the entry will contain the 042 * property name for the key but will have a null entry value. 043 * </p> 044 * <p> 045 * The use of the map also ensures that a single property appears only once in the request (it either has new values or it is to 046 * be removed). 047 * </p> 048 * <p> 049 * Note that the number of values in a property (e.g., {@link Property#size()}, {@link Property#isEmpty()}, 050 * {@link Property#isSingle()}, and {@link Property#isMultiple()}) has no influence on whether the property should be removed. It 051 * is possible for a property to have no values. 052 * </p> 053 * 054 * @author Randall Hauch 055 */ 056 public class UpdatePropertiesRequest extends ChangeRequest { 057 058 private static final long serialVersionUID = 1L; 059 060 private final Location on; 061 private final String workspaceName; 062 private final Map<Name, Property> properties; 063 private Location actualLocation; 064 065 /** 066 * Create a request to update the properties on the node at the supplied location. 067 * 068 * @param on the location of the node to be read 069 * @param workspaceName the name of the workspace containing the node 070 * @param properties the map of properties (keyed by their name), which is reused without copying 071 * @throws IllegalArgumentException if the location or workspace name is null or if there are no properties to update 072 */ 073 public UpdatePropertiesRequest( Location on, 074 String workspaceName, 075 Map<Name, Property> properties ) { 076 CheckArg.isNotNull(on, "on"); 077 CheckArg.isNotEmpty(properties, "properties"); 078 CheckArg.isNotNull(workspaceName, "workspaceName"); 079 this.workspaceName = workspaceName; 080 this.on = on; 081 this.properties = Collections.unmodifiableMap(properties); 082 } 083 084 /** 085 * {@inheritDoc} 086 * 087 * @see org.jboss.dna.graph.request.Request#isReadOnly() 088 */ 089 @Override 090 public boolean isReadOnly() { 091 return false; 092 } 093 094 /** 095 * Get the location defining the node that is to be updated. 096 * 097 * @return the location of the node; never null 098 */ 099 public Location on() { 100 return on; 101 } 102 103 /** 104 * Get the name of the workspace in which the node exists. 105 * 106 * @return the name of the workspace; never null 107 */ 108 public String inWorkspace() { 109 return workspaceName; 110 } 111 112 /** 113 * Get the map of properties for the node, keyed by property name. Any property to be removed will have a map entry with a 114 * null value. 115 * 116 * @return the properties being updated; never null and never empty 117 */ 118 public Map<Name, Property> properties() { 119 return properties; 120 } 121 122 /** 123 * Sets the actual and complete location of the node being updated. This method must be called when processing the request, 124 * and the actual location must have a {@link Location#getPath() path}. 125 * 126 * @param actual the actual location of the node being updated, or null if the {@link #on() current location} should be used 127 * @throws IllegalArgumentException if the actual location does represent the {@link Location#isSame(Location) same location} 128 * as the {@link #on() current location}, or if the actual location does not have a path. 129 * @throws IllegalStateException if the request is frozen 130 */ 131 public void setActualLocationOfNode( Location actual ) { 132 checkNotFrozen(); 133 if (!on.isSame(actual)) { // not same if actual is null 134 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, on)); 135 } 136 assert actual != null; 137 if (!actual.hasPath()) { 138 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual)); 139 } 140 this.actualLocation = actual; 141 } 142 143 /** 144 * Get the actual location of the node that was updated. 145 * 146 * @return the actual location, or null if the actual location was not set 147 */ 148 public Location getActualLocationOfNode() { 149 return actualLocation; 150 } 151 152 /** 153 * {@inheritDoc} 154 * 155 * @see org.jboss.dna.graph.request.ChangeRequest#changes(java.lang.String, org.jboss.dna.graph.property.Path) 156 */ 157 @Override 158 public boolean changes( String workspace, 159 Path path ) { 160 return this.workspaceName.equals(workspace) && on.hasPath() && on.getPath().isAtOrBelow(path); 161 } 162 163 /** 164 * {@inheritDoc} 165 * 166 * @see org.jboss.dna.graph.request.Request#cancel() 167 */ 168 @Override 169 public void cancel() { 170 super.cancel(); 171 this.actualLocation = null; 172 } 173 174 /** 175 * {@inheritDoc} 176 * 177 * @see java.lang.Object#hashCode() 178 */ 179 @Override 180 public int hashCode() { 181 return HashCode.compute(on, workspaceName); 182 } 183 184 /** 185 * {@inheritDoc} 186 * 187 * @see java.lang.Object#equals(java.lang.Object) 188 */ 189 @Override 190 public boolean equals( Object obj ) { 191 if (obj == this) return true; 192 if (this.getClass().isInstance(obj)) { 193 UpdatePropertiesRequest that = (UpdatePropertiesRequest)obj; 194 if (!this.on().equals(that.on())) return false; 195 if (!this.properties().equals(that.properties())) return false; 196 if (!this.inWorkspace().equals(that.inWorkspace())) return false; 197 return true; 198 } 199 return false; 200 } 201 202 /** 203 * {@inheritDoc} 204 * 205 * @see org.jboss.dna.graph.request.ChangeRequest#changedLocation() 206 */ 207 @Override 208 public Location changedLocation() { 209 return on; 210 } 211 212 /** 213 * {@inheritDoc} 214 * 215 * @see org.jboss.dna.graph.request.ChangeRequest#changedWorkspace() 216 */ 217 @Override 218 public String changedWorkspace() { 219 return workspaceName; 220 } 221 222 /** 223 * {@inheritDoc} 224 * 225 * @see java.lang.Object#toString() 226 */ 227 @Override 228 public String toString() { 229 return "update properties on " + on() + " in the \"" + workspaceName + "\" workspace to " + properties(); 230 } 231 232 }