View Javadoc

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 }