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 {
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 if (!on.equals(actual)) { // not same if actual is null
135 throw new IllegalArgumentException(GraphI18n.actualLocationNotEqualToInputLocation.text(actual, on));
136 }
137 assert actual != null;
138 if (!actual.hasPath()) {
139 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
140 }
141 this.actualLocation = actual;
142 assert actualLocation != null;
143
144 assert actualAddedValues != null;
145 assert actualAddedValues.size() <= addedValues.size();
146 assert actualRemovedValues != null;
147 assert actualRemovedValues.size() <= actualRemovedValues.size();
148
149 this.actualAddedValues = actualAddedValues;
150 this.actualRemovedValues = actualRemovedValues;
151 }
152
153 /**
154 * Record that the property did not exist prior to the processing of this request and was actually created by this request.
155 * This method must be called when processing the request, and the actual location must have a {@link Location#getPath() path}.
156 *
157 * @param property the property being created or updated (may not be <code>null</code>)
158 * @param created true if the property was created by this request, or false if this request updated an existing property
159 * @throws IllegalStateException if the request is frozen
160 * @throws IllegalArgumentException if the property is <code>null</code>
161 */
162 public void setActualProperty( Property property,
163 boolean created ) {
164 CheckArg.isNotNull(property, "property");
165 checkNotFrozen();
166 this.actualProperty = property;
167 this.actualCreation = created;
168 }
169
170 /**
171 * Get the actual node property that was created or updated.
172 *
173 * @return the actual property or <code>null</code> if the actual property was not set
174 */
175 public Property getActualProperty() {
176 return this.actualProperty;
177 }
178
179 /**
180 * Get the actual location of the node that was updated.
181 *
182 * @return the actual location, or null if the actual location was not set
183 */
184 public Location getActualLocationOfNode() {
185 return actualLocation;
186 }
187
188 /**
189 * Get the actual added values. This should always be identical to the list of values that were requested to be added.
190 *
191 * @return the values that were added to the node when this request was processed; never null
192 */
193 public List<Object> getActualAddedValues() {
194 return actualAddedValues;
195 }
196
197 /**
198 * Get the actual removed values. This will differ from the values that were requested to be removed if some of the values
199 * that were requested to be removed were not already values for the property.
200 *
201 * @return the values that were removed from the node when this request was processed; never null
202 */
203 public List<Object> getActualRemovedValues() {
204 return actualRemovedValues;
205 }
206
207 /**
208 * Get whether the {@link #property() property} was created.
209 *
210 * @return true if this request created the property, or false if this request changed an existing property
211 */
212 public boolean isNewProperty() {
213 return actualCreation;
214 }
215
216 /**
217 * {@inheritDoc}
218 * <p>
219 * This method does not clone the results.
220 * </p>
221 *
222 * @see org.modeshape.graph.request.ChangeRequest#clone()
223 */
224 @Override
225 public UpdateValuesRequest clone() {
226 UpdateValuesRequest request = new UpdateValuesRequest(workspaceName, actualLocation != null ? actualLocation : on,
227 propertyName, addedValues, removedValues);
228 request.setActualLocation(actualLocation, actualAddedValues, actualRemovedValues);
229
230 // don't call request.setActualProperty(Property, boolean) here as the actual property may have not been set
231 request.actualProperty = actualProperty;
232 request.actualCreation = actualCreation;
233
234 return request;
235 }
236
237 @Override
238 public RequestType getType() {
239 return RequestType.UPDATE_VALUES;
240 }
241 }