1 /*
2 * ModeShape (http://www.modeshape.org)
3 * See the COPYRIGHT.txt file distributed with this work for information
4 * regarding copyright ownership. Some portions may be licensed
5 * to Red Hat, Inc. under one or more contributor license agreements.
6 * See the AUTHORS.txt file in the distribution for a full listing of
7 * individual contributors.
8 *
9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10 * is licensed to you under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * ModeShape is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this software; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23 */
24 package org.modeshape.graph.request;
25
26 import java.util.LinkedList;
27 import java.util.List;
28 import org.modeshape.common.util.CheckArg;
29 import org.modeshape.common.util.HashCode;
30 import org.modeshape.graph.GraphI18n;
31 import org.modeshape.graph.Location;
32 import org.modeshape.graph.connector.RepositoryConnection;
33 import org.modeshape.graph.property.Path;
34 import org.modeshape.graph.property.Property;
35
36 /**
37 * Instruction to read a block of the children of a node, where the block is dictated by the {@link #startingAfter location of the
38 * child preceding the block} and the {@link #count() maximum number of children} to include in the block. This command is useful
39 * when paging through a large number of children, when the previous block of children was already retrieved and the next block is
40 * to be read.
41 *
42 * @see ReadBlockOfChildrenRequest
43 */
44 public class ReadNextBlockOfChildrenRequest extends CacheableRequest {
45
46 public static final int INDEX_NOT_USED = -1;
47
48 private static final long serialVersionUID = 1L;
49
50 private final List<Location> children = new LinkedList<Location>();
51 private final Location startingAfter;
52 private final String workspaceName;
53 private final int count;
54 private Location actualStartingAfter;
55
56 /**
57 * Create a request to read those children of a node that are immediately after a supplied sibling node.
58 *
59 * @param startingAfter the location of the previous sibling that was the last child of the previous block of children read
60 * @param workspaceName the name of the workspace containing the node
61 * @param count the maximum number of children that should be included in the block
62 * @throws IllegalArgumentException if the workspace name or <code>startingAfter</code> location is null, or if
63 * <code>count</count> is less than 1.
64 */
65 public ReadNextBlockOfChildrenRequest( Location startingAfter,
66 String workspaceName,
67 int count ) {
68 CheckArg.isNotNull(startingAfter, "startingAfter");
69 CheckArg.isPositive(count, "count");
70 CheckArg.isNotNull(workspaceName, "workspaceName");
71 this.workspaceName = workspaceName;
72 this.startingAfter = startingAfter;
73 this.count = count;
74 }
75
76 /**
77 * {@inheritDoc}
78 *
79 * @see org.modeshape.graph.request.Request#isReadOnly()
80 */
81 @Override
82 public boolean isReadOnly() {
83 return true;
84 }
85
86 /**
87 * Get the maximum number of children that may be returned in the block.
88 *
89 * @return the block's maximum count
90 * @see #startingAfter()
91 */
92 public int count() {
93 return this.count;
94 }
95
96 /**
97 * Get the location of the child after which the block begins. This form may be easier to use when paging through blocks, as
98 * the last children retrieved with the previous block can be supplied with the next read request.
99 *
100 * @return the location of the child that is immediately before the start of the block; index at which this block starts;
101 * never negative
102 * @see #count()
103 */
104 public Location startingAfter() {
105 return this.startingAfter;
106 }
107
108 /**
109 * Get the name of the workspace in which the parent and children exist.
110 *
111 * @return the name of the workspace; never null
112 */
113 public String inWorkspace() {
114 return workspaceName;
115 }
116
117 /**
118 * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
119 * represented by a location.
120 *
121 * @return the children that were read; never null
122 */
123 public List<Location> getChildren() {
124 return children;
125 }
126
127 /**
128 * Add to the list of children that has been read the supplied children with the given path and identification properties. The
129 * children are added in order.
130 *
131 * @param children the locations of the children that were read
132 * @throws IllegalArgumentException if the parameter is null
133 * @throws IllegalStateException if the request is frozen
134 * @see #addChild(Location)
135 * @see #addChild(Path, Property)
136 * @see #addChild(Path, Property, Property...)
137 */
138 public void addChildren( Iterable<Location> children ) {
139 checkNotFrozen();
140 CheckArg.isNotNull(children, "children");
141 for (Location child : children) {
142 if (child != null) this.children.add(child);
143 }
144 }
145
146 /**
147 * Add to the list of children that has been read the child with the given path and identification properties. The children
148 * should be added in order.
149 *
150 * @param child the location of the child that was read
151 * @throws IllegalArgumentException if the location is null
152 * @throws IllegalStateException if the request is frozen
153 * @see #addChild(Path, Property)
154 * @see #addChild(Path, Property, Property...)
155 */
156 public void addChild( Location child ) {
157 checkNotFrozen();
158 CheckArg.isNotNull(child, "child");
159 this.children.add(child);
160 }
161
162 /**
163 * Add to the list of children that has been read the child with the given path and identification properties. The children
164 * should be added in order.
165 *
166 * @param pathToChild the path of the child that was just read
167 * @param firstIdProperty the first identification property of the child that was just read
168 * @param remainingIdProperties the remaining identification properties of the child that was just read
169 * @throws IllegalArgumentException if the path or identification properties are null
170 * @throws IllegalStateException if the request is frozen
171 * @see #addChild(Location)
172 * @see #addChild(Path, Property)
173 */
174 public void addChild( Path pathToChild,
175 Property firstIdProperty,
176 Property... remainingIdProperties ) {
177 checkNotFrozen();
178 Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
179 this.children.add(child);
180 }
181
182 /**
183 * Add to the list of children that has been read the child with the given path and identification property. The children
184 * should be added in order.
185 *
186 * @param pathToChild the path of the child that was just read
187 * @param idProperty the identification property of the child that was just read
188 * @throws IllegalArgumentException if the path or identification properties are null
189 * @throws IllegalStateException if the request is frozen
190 * @see #addChild(Location)
191 * @see #addChild(Path, Property, Property...)
192 */
193 public void addChild( Path pathToChild,
194 Property idProperty ) {
195 checkNotFrozen();
196 Location child = Location.create(pathToChild, idProperty);
197 this.children.add(child);
198 }
199
200 /**
201 * Sets the actual and complete location of the node whose children have been read. This method must be called when processing
202 * the request, and the actual location must have a {@link Location#getPath() path}.
203 *
204 * @param actual the actual location of the node being read, or null if the {@link #startingAfter() starting after location}
205 * should be used
206 * @throws IllegalArgumentException if the actual location is null or does not have a path.
207 * @throws IllegalStateException if the request is frozen
208 */
209 public void setActualLocationOfStartingAfterNode( Location actual ) {
210 checkNotFrozen();
211 CheckArg.isNotNull(actual, "actual");
212 if (!actual.hasPath()) {
213 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
214 }
215 this.actualStartingAfter = actual;
216 }
217
218 /**
219 * Get the actual location of the {@link #startingAfter() starting after} sibling.
220 *
221 * @return the actual location, or null if the actual location was not set
222 */
223 public Location getActualLocationOfStartingAfterNode() {
224 return actualStartingAfter;
225 }
226
227 /**
228 * {@inheritDoc}
229 *
230 * @see org.modeshape.graph.request.Request#cancel()
231 */
232 @Override
233 public void cancel() {
234 super.cancel();
235 this.actualStartingAfter = null;
236 this.children.clear();
237 }
238
239 /**
240 * {@inheritDoc}
241 *
242 * @see java.lang.Object#hashCode()
243 */
244 @Override
245 public int hashCode() {
246 return HashCode.compute(startingAfter, workspaceName);
247 }
248
249 /**
250 * {@inheritDoc}
251 *
252 * @see java.lang.Object#equals(java.lang.Object)
253 */
254 @Override
255 public boolean equals( Object obj ) {
256 if (obj == this) return true;
257 if (this.getClass().isInstance(obj)) {
258 ReadNextBlockOfChildrenRequest that = (ReadNextBlockOfChildrenRequest)obj;
259 if (!this.startingAfter().isSame(that.startingAfter())) return false;
260 if (this.count() != that.count()) return false;
261 if (!this.inWorkspace().equals(that.inWorkspace())) return false;
262 return true;
263 }
264 return false;
265 }
266
267 /**
268 * {@inheritDoc}
269 *
270 * @see java.lang.Object#toString()
271 */
272 @Override
273 public String toString() {
274 if (count() == 1) {
275 return "read the next child after " + startingAfter() + " in the \"" + workspaceName + "\" workspace";
276 }
277 return "read the next " + count() + " children after " + startingAfter() + " in the \"" + workspaceName + "\" workspace";
278 }
279
280 @Override
281 public RequestType getType() {
282 return RequestType.READ_NEXT_BLOCK_OF_CHILDREN;
283 }
284 }