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.text.Inflector;
29  import org.modeshape.common.util.CheckArg;
30  import org.modeshape.common.util.HashCode;
31  import org.modeshape.graph.GraphI18n;
32  import org.modeshape.graph.Location;
33  import org.modeshape.graph.connector.RepositoryConnection;
34  import org.modeshape.graph.property.Path;
35  import org.modeshape.graph.property.Property;
36  
37  /**
38   * Instruction to read a block of the children of a node, where the block is dictated by the {@link #startingAtIndex() starting
39   * index} and the {@link #count() maximum number of children} to include in the block. This command is useful when paging through
40   * a large number of children.
41   * 
42   * @see ReadNextBlockOfChildrenRequest
43   */
44  public class ReadBlockOfChildrenRequest extends CacheableRequest {
45  
46      public static final int INDEX_NOT_USED = -1;
47  
48      private static final long serialVersionUID = 1L;
49  
50      private final Location of;
51      private final String workspaceName;
52      private final List<Location> children = new LinkedList<Location>();
53      private final int startingAtIndex;
54      private final int count;
55      private Location actualLocation;
56  
57      /**
58       * Create a request to read a block of the children of a node at the supplied location. The block is defined by the starting
59       * index of the first child and the number of children to include. Note that this index is <i>not</i> the
60       * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index of the child as if the children were in
61       * an array.
62       * 
63       * @param of the location of the node whose children are to be read
64       * @param workspaceName the name of the workspace containing the parent
65       * @param startingIndex the zero-based index of the first child to be included in the block
66       * @param count the maximum number of children that should be included in the block
67       * @throws IllegalArgumentException if the location or workspace name is null, if <code>startingIndex</code> is negative, or
68       *         if <code>count</count> is less than 1.
69       */
70      public ReadBlockOfChildrenRequest( Location of,
71                                         String workspaceName,
72                                         int startingIndex,
73                                         int count ) {
74          CheckArg.isNotNull(of, "of");
75          CheckArg.isNonNegative(startingIndex, "startingIndex");
76          CheckArg.isPositive(count, "count");
77          CheckArg.isNotNull(workspaceName, "workspaceName");
78          this.workspaceName = workspaceName;
79          this.of = of;
80          this.startingAtIndex = startingIndex;
81          this.count = count;
82      }
83  
84      /**
85       * {@inheritDoc}
86       * 
87       * @see org.modeshape.graph.request.Request#isReadOnly()
88       */
89      @Override
90      public boolean isReadOnly() {
91          return true;
92      }
93  
94      /**
95       * Get the location defining the node whose children are to be read.
96       * 
97       * @return the location of the parent node; never null
98       */
99      public Location of() {
100         return of;
101     }
102 
103     /**
104      * Get the name of the workspace in which the parent and children exist.
105      * 
106      * @return the name of the workspace; never null
107      */
108     public String inWorkspace() {
109         return workspaceName;
110     }
111 
112     /**
113      * Get the maximum number of children that may be returned in the block.
114      * 
115      * @return the block's maximum count
116      * @see #startingAtIndex()
117      * @see #endingBefore()
118      */
119     public int count() {
120         return this.count;
121     }
122 
123     /**
124      * Get the starting index of the block, which is the index of the first child to include. This index corresponds to the index
125      * of all children in the list, not the {@link Path.Segment#getIndex() same-name-sibiling index}.
126      * 
127      * @return the (zero-based) child index at which this block starts; never negative and always less than
128      *         {@link #endingBefore()}
129      * @see #endingBefore()
130      * @see #count()
131      */
132     public int startingAtIndex() {
133         return this.startingAtIndex;
134     }
135 
136     /**
137      * Get the index past the last child that is to be included in the block. This index corresponds to the index of all children
138      * in the list, not the {@link Path.Segment#getIndex() same-name-sibiling index}.
139      * 
140      * @return the index just past the last child included in the block; always positive and always greater than
141      *         {@link #startingAtIndex()}.
142      * @see #startingAtIndex()
143      * @see #count()
144      */
145     public int endingBefore() {
146         return this.startingAtIndex + this.count;
147     }
148 
149     /**
150      * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
151      * represented by a location.
152      * 
153      * @return the children that were read; never null
154      */
155     public List<Location> getChildren() {
156         return children;
157     }
158 
159     /**
160      * Add to the list of children that has been read the supplied children with the given path and identification properties. The
161      * children are added in order.
162      * 
163      * @param children the locations of the children that were read
164      * @throws IllegalArgumentException if the parameter is null
165      * @throws IllegalStateException if the request is frozen
166      * @see #addChild(Location)
167      * @see #addChild(Path, Property)
168      * @see #addChild(Path, Property, Property...)
169      */
170     public void addChildren( Iterable<Location> children ) {
171         checkNotFrozen();
172         CheckArg.isNotNull(children, "children");
173         for (Location child : children) {
174             if (child != null) this.children.add(child);
175         }
176     }
177 
178     /**
179      * Add to the list of children that has been read the child with the given path and identification properties. The children
180      * should be added in order.
181      * 
182      * @param child the location of the child that was read
183      * @throws IllegalArgumentException if the location is null
184      * @throws IllegalStateException if the request is frozen
185      * @see #addChild(Path, Property)
186      * @see #addChild(Path, Property, Property...)
187      */
188     public void addChild( Location child ) {
189         checkNotFrozen();
190         CheckArg.isNotNull(child, "child");
191         this.children.add(child);
192     }
193 
194     /**
195      * Add to the list of children that has been read the child with the given path and identification properties. The children
196      * should be added in order.
197      * 
198      * @param pathToChild the path of the child that was just read
199      * @param firstIdProperty the first identification property of the child that was just read
200      * @param remainingIdProperties the remaining identification properties of the child that was just read
201      * @throws IllegalArgumentException if the path or identification properties are null
202      * @throws IllegalStateException if the request is frozen
203      * @see #addChild(Location)
204      * @see #addChild(Path, Property)
205      */
206     public void addChild( Path pathToChild,
207                           Property firstIdProperty,
208                           Property... remainingIdProperties ) {
209         checkNotFrozen();
210         Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties);
211         this.children.add(child);
212     }
213 
214     /**
215      * Add to the list of children that has been read the child with the given path and identification property. The children
216      * should be added in order.
217      * 
218      * @param pathToChild the path of the child that was just read
219      * @param idProperty the identification property of the child that was just read
220      * @throws IllegalArgumentException if the path or identification properties are null
221      * @throws IllegalStateException if the request is frozen
222      * @see #addChild(Location)
223      * @see #addChild(Path, Property, Property...)
224      */
225     public void addChild( Path pathToChild,
226                           Property idProperty ) {
227         checkNotFrozen();
228         Location child = Location.create(pathToChild, idProperty);
229         this.children.add(child);
230     }
231 
232     /**
233      * Sets the actual and complete location of the node whose children have been read. This method must be called when processing
234      * the request, and the actual location must have a {@link Location#getPath() path}.
235      * 
236      * @param actual the actual location of the node being read, or null if the {@link #of() current location} should be used
237      * @throws IllegalArgumentException if the actual location is null or does not have a path.
238      * @throws IllegalStateException if the request is frozen
239      */
240     public void setActualLocationOfNode( Location actual ) {
241         checkNotFrozen();
242         CheckArg.isNotNull(actual, "actual");
243         if (!actual.hasPath()) {
244             throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
245         }
246         this.actualLocation = actual;
247     }
248 
249     /**
250      * Get the actual location of the node whose children were read.
251      * 
252      * @return the actual location, or null if the actual location was not set
253      */
254     public Location getActualLocationOfNode() {
255         return actualLocation;
256     }
257 
258     /**
259      * {@inheritDoc}
260      * 
261      * @see org.modeshape.graph.request.Request#cancel()
262      */
263     @Override
264     public void cancel() {
265         super.cancel();
266         this.actualLocation = null;
267         this.children.clear();
268     }
269 
270     /**
271      * {@inheritDoc}
272      * 
273      * @see java.lang.Object#hashCode()
274      */
275     @Override
276     public int hashCode() {
277         return HashCode.compute(of, workspaceName);
278     }
279 
280     /**
281      * {@inheritDoc}
282      * 
283      * @see java.lang.Object#equals(java.lang.Object)
284      */
285     @Override
286     public boolean equals( Object obj ) {
287         if (obj == this) return true;
288         if (this.getClass().isInstance(obj)) {
289             ReadBlockOfChildrenRequest that = (ReadBlockOfChildrenRequest)obj;
290             if (!this.of().isSame(that.of())) return false;
291             if (this.startingAtIndex() != that.startingAtIndex()) return false;
292             if (this.count() != that.count()) return false;
293             if (!this.inWorkspace().equals(that.inWorkspace())) return false;
294             return true;
295         }
296         return false;
297     }
298 
299     /**
300      * {@inheritDoc}
301      * 
302      * @see java.lang.Object#toString()
303      */
304     @Override
305     public String toString() {
306         Inflector inflector = Inflector.getInstance();
307         if (count() == 1) {
308             return "read " + inflector.ordinalize(startingAtIndex()) + " thru " + inflector.ordinalize(endingBefore() - 1)
309                    + " children of " + of() + " in the \"" + workspaceName + "\" workspace";
310         }
311         return "read " + inflector.ordinalize(startingAtIndex()) + " child of " + of() + " in the \"" + workspaceName
312                + "\" workspace";
313     }
314 
315     @Override
316     public RequestType getType() {
317         return RequestType.READ_BLOCK_OF_CHILDREN;
318     }
319 }