1 package org.modeshape.graph.request;
2
3 import java.util.Collections;
4 import java.util.List;
5 import org.modeshape.common.util.CheckArg;
6 import org.modeshape.graph.GraphI18n;
7 import org.modeshape.graph.Location;
8 import org.modeshape.graph.property.Name;
9 import org.modeshape.graph.property.Path;
10 import org.modeshape.graph.property.Property;
11
12 /**
13 * Instruction to update the values for a certain property on the node at the specified location.
14 * <p>
15 * This request is capable of specifying specific values for the property that will be added or removed. Other values for the
16 * property not be affected by this request. The request contains a workspace name and a location that uniquely identify a node in
17 * the workspace as well as the name of property (that may or may not previously exist) on the node. The request also contains
18 * zero or more values to add and zero or more values to remove from the property. All values will be appended to the list of
19 * values. Removals are processed before additions.
20 * </p>
21 * <p>
22 * Even if the property has no values after this call, the property itself will not be removed by this request.
23 * </p>
24 * <p>
25 * Note that the number of values in a property (e.g., {@link Property#size()}, {@link Property#isEmpty()},
26 * {@link Property#isSingle()}, and {@link Property#isMultiple()}) has no influence on whether the property should be removed. It
27 * is possible for a property to have no values.
28 * </p>
29 */
30 public class UpdateValuesRequest extends ChangeRequest implements PropertyChangeRequest {
31
32 private static final long serialVersionUID = 1L;
33
34 private final String workspaceName;
35 private final Location on;
36 private final Name propertyName;
37 private final List<Object> addedValues;
38 private final List<Object> removedValues;
39
40 private Location actualLocation;
41 private List<Object> actualAddedValues;
42 private List<Object> actualRemovedValues;
43 private boolean actualCreation;
44 private Property actualProperty;
45
46 public UpdateValuesRequest( String workspaceName,
47 Location on,
48 Name propertyName,
49 List<Object> addedValues,
50 List<Object> removedValues ) {
51 super();
52
53 assert workspaceName != null;
54 assert on != null;
55 assert propertyName != null;
56
57 this.workspaceName = workspaceName;
58 this.on = on;
59 this.propertyName = propertyName;
60 this.addedValues = addedValues == null ? Collections.emptyList() : addedValues;
61 this.removedValues = removedValues == null ? Collections.emptyList() : removedValues;
62 }
63
64 /**
65 * Get the location defining the node that is to be updated.
66 *
67 * @return the location of the node; never null
68 */
69 public Location on() {
70 return on;
71 }
72
73 /**
74 * Get the name of the property that is to be updated.
75 *
76 * @return the name of the property; never null
77 */
78 public Name property() {
79 return propertyName;
80 }
81
82 /**
83 * Get the name of the workspace in which the node exists.
84 *
85 * @return the name of the workspace; never null
86 */
87 public String inWorkspace() {
88 return workspaceName;
89 }
90
91 /**
92 * Get the list of values to be added.
93 *
94 * @return the values (if any) to be added; never null
95 */
96 public List<Object> addedValues() {
97 return addedValues;
98 }
99
100 /**
101 * Get the list of values to be removed.
102 *
103 * @return the values (if any) to be removed; never null
104 */
105 public List<Object> removedValues() {
106 return removedValues;
107 }
108
109 @Override
110 public Location changedLocation() {
111 return on;
112 }
113
114 @Override
115 public String changedWorkspace() {
116 return workspaceName;
117 }
118
119 @Override
120 public boolean changes( String workspace,
121 Path path ) {
122 return workspaceName.equals(workspace) && on.hasPath() && on.getPath().equals(path);
123 }
124
125 @Override
126 public boolean isReadOnly() {
127 return addedValues.isEmpty() && removedValues.isEmpty();
128 }
129
130 public void setActualLocation( Location actual,
131 List<Object> actualAddedValues,
132 List<Object> actualRemovedValues ) {
133 checkNotFrozen();
134 CheckArg.isNotNull(actual, "actual");
135 if (!actual.hasPath()) {
136 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
137 }
138 this.actualLocation = actual;
139 assert actualLocation != null;
140
141 assert actualAddedValues != null;
142 assert actualAddedValues.size() <= addedValues.size();
143 assert actualRemovedValues != null;
144 assert actualRemovedValues.size() <= actualRemovedValues.size();
145
146 this.actualAddedValues = actualAddedValues;
147 this.actualRemovedValues = actualRemovedValues;
148 }
149
150 /**
151 * Record that the property did not exist prior to the processing of this request and was actually created by this request.
152 * This method must be called when processing the request, and the actual location must have a {@link Location#getPath() path}
153 * .
154 *
155 * @param property the property being created or updated (may not be <code>null</code>)
156 * @param created true if the property was created by this request, or false if this request updated an existing property
157 * @throws IllegalStateException if the request is frozen
158 * @throws IllegalArgumentException if the property is <code>null</code>
159 */
160 public void setActualProperty( Property property,
161 boolean created ) {
162 CheckArg.isNotNull(property, "property");
163 checkNotFrozen();
164 this.actualProperty = property;
165 this.actualCreation = created;
166 }
167
168 /**
169 * Get the actual node property that was created or updated.
170 *
171 * @return the actual property or <code>null</code> if the actual property was not set
172 */
173 public Property getActualProperty() {
174 return this.actualProperty;
175 }
176
177 /**
178 * Get the actual location of the node that was updated.
179 *
180 * @return the actual location, or null if the actual location was not set
181 */
182 public Location getActualLocationOfNode() {
183 return actualLocation;
184 }
185
186 /**
187 * Get the actual added values. This should always be identical to the list of values that were requested to be added.
188 *
189 * @return the values that were added to the node when this request was processed; never null
190 */
191 public List<Object> getActualAddedValues() {
192 return actualAddedValues;
193 }
194
195 /**
196 * Get the actual removed values. This will differ from the values that were requested to be removed if some of the values
197 * that were requested to be removed were not already values for the property.
198 *
199 * @return the values that were removed from the node when this request was processed; never null
200 */
201 public List<Object> getActualRemovedValues() {
202 return actualRemovedValues;
203 }
204
205 /**
206 * Get whether the {@link #property() property} was created.
207 *
208 * @return true if this request created the property, or false if this request changed an existing property
209 */
210 public boolean isNewProperty() {
211 return actualCreation;
212 }
213
214 /**
215 * {@inheritDoc}
216 * <p>
217 * This method does not clone the results.
218 * </p>
219 *
220 * @see org.modeshape.graph.request.ChangeRequest#clone()
221 */
222 @Override
223 public UpdateValuesRequest clone() {
224 UpdateValuesRequest request = new UpdateValuesRequest(workspaceName, actualLocation != null ? actualLocation : on,
225 propertyName, addedValues, removedValues);
226 request.setActualLocation(actualLocation, actualAddedValues, actualRemovedValues);
227
228 // don't call request.setActualProperty(Property, boolean) here as the actual property may have not been set
229 request.actualProperty = actualProperty;
230 request.actualCreation = actualCreation;
231
232 return request;
233 }
234
235 @Override
236 public RequestType getType() {
237 return RequestType.UPDATE_VALUES;
238 }
239 }