001 /*
002 * JBoss, Home of Professional Open Source.
003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004 * as indicated by the @author tags. See the copyright.txt file in the
005 * distribution for a full listing of individual contributors.
006 *
007 * This is free software; you can redistribute it and/or modify it
008 * under the terms of the GNU Lesser General Public License as
009 * published by the Free Software Foundation; either version 2.1 of
010 * the License, or (at your option) any later version.
011 *
012 * This software is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this software; if not, write to the Free
019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021 */
022 package org.jboss.dna.graph.requests;
023
024 import java.util.LinkedList;
025 import java.util.List;
026 import org.jboss.dna.common.text.Inflector;
027 import org.jboss.dna.common.util.CheckArg;
028 import org.jboss.dna.graph.GraphI18n;
029 import org.jboss.dna.graph.Location;
030 import org.jboss.dna.graph.connectors.RepositoryConnection;
031 import org.jboss.dna.graph.properties.Path;
032 import org.jboss.dna.graph.properties.Property;
033
034 /**
035 * Instruction to read a block of the children of a node, where the block is dictated by the {@link #startingAtIndex() starting
036 * index} and the {@link #count() maximum number of children} to include in the block. This command is useful when paging through
037 * a large number of children.
038 *
039 * @see ReadNextBlockOfChildrenRequest
040 * @author Randall Hauch
041 */
042 public class ReadBlockOfChildrenRequest extends CacheableRequest {
043
044 public static final int INDEX_NOT_USED = -1;
045
046 private static final long serialVersionUID = 1L;
047
048 private final Location of;
049 private final List<Location> children = new LinkedList<Location>();
050 private final int startingAtIndex;
051 private final int count;
052 private Location actualLocation;
053
054 /**
055 * Create a request to read a block of the children of a node at the supplied location. The block is defined by the starting
056 * index of the first child and the number of children to include. Note that this index is <i>not</i> the
057 * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index of the child as if the children were in
058 * an array.
059 *
060 * @param of the location of the node whose children are to be read
061 * @param startingIndex the index of the first child to be included in the block
062 * @param count the maximum number of children that should be included in the block
063 * @throws IllegalArgumentException if the location is null, if <code>startingIndex</code> is negative, or if
064 * <code>count</count> is less than 1.
065 */
066 public ReadBlockOfChildrenRequest( Location of,
067 int startingIndex,
068 int count ) {
069 CheckArg.isNotNull(of, "of");
070 CheckArg.isNonNegative(startingIndex, "startingIndex");
071 CheckArg.isPositive(count, "count");
072 this.of = of;
073 this.startingAtIndex = startingIndex;
074 this.count = count;
075 }
076
077 /**
078 * {@inheritDoc}
079 *
080 * @see org.jboss.dna.graph.requests.Request#isReadOnly()
081 */
082 @Override
083 public boolean isReadOnly() {
084 return true;
085 }
086
087 /**
088 * Get the location defining the node whose children are to be read.
089 *
090 * @return the location of the parent node; never null
091 */
092 public Location of() {
093 return of;
094 }
095
096 /**
097 * Get the maximum number of children that may be returned in the block.
098 *
099 * @return the block's maximum count
100 * @see #startingAtIndex()
101 * @see #endingBefore()
102 */
103 public int count() {
104 return this.count;
105 }
106
107 /**
108 * Get the starting index of the block, which is the index of the first child to include. This index corresponds to the index
109 * of all children in the list, not the {@link Path.Segment#getIndex() same-name-sibiling index}.
110 *
111 * @return the child index at which this block starts; never negative and always less than {@link #endingBefore()}
112 * @see #endingBefore()
113 * @see #count()
114 */
115 public int startingAtIndex() {
116 return this.startingAtIndex;
117 }
118
119 /**
120 * Get the index past the last child that is to be included in the block. This index corresponds to the index of all children
121 * in the list, not the {@link Path.Segment#getIndex() same-name-sibiling index}.
122 *
123 * @return the index just past the last child included in the block; always positive and always greater than
124 * {@link #startingAtIndex()}.
125 * @see #startingAtIndex()
126 * @see #count()
127 */
128 public int endingBefore() {
129 return this.startingAtIndex + this.count;
130 }
131
132 /**
133 * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is
134 * represented by a location.
135 *
136 * @return the children that were read; never null
137 */
138 public List<Location> getChildren() {
139 return children;
140 }
141
142 /**
143 * Add to the list of children that has been read the child with the given path and identification properties. The children
144 * should be added in order.
145 *
146 * @param child the location of the child that was read
147 * @throws IllegalArgumentException if the location is null
148 * @see #addChild(Path, Property)
149 * @see #addChild(Path, Property, Property...)
150 */
151 public void addChild( Location child ) {
152 CheckArg.isNotNull(child, "child");
153 this.children.add(child);
154 }
155
156 /**
157 * Add to the list of children that has been read the child with the given path and identification properties. The children
158 * should be added in order.
159 *
160 * @param pathToChild the path of the child that was just read
161 * @param firstIdProperty the first identification property of the child that was just read
162 * @param remainingIdProperties the remaining identification properties of the child that was just read
163 * @throws IllegalArgumentException if the path or identification properties are null
164 * @see #addChild(Location)
165 * @see #addChild(Path, Property)
166 */
167 public void addChild( Path pathToChild,
168 Property firstIdProperty,
169 Property... remainingIdProperties ) {
170 Location child = new Location(pathToChild, firstIdProperty, remainingIdProperties);
171 this.children.add(child);
172 }
173
174 /**
175 * Add to the list of children that has been read the child with the given path and identification property. The children
176 * should be added in order.
177 *
178 * @param pathToChild the path of the child that was just read
179 * @param idProperty the identification property of the child that was just read
180 * @throws IllegalArgumentException if the path or identification properties are null
181 * @see #addChild(Location)
182 * @see #addChild(Path, Property, Property...)
183 */
184 public void addChild( Path pathToChild,
185 Property idProperty ) {
186 Location child = new Location(pathToChild, idProperty);
187 this.children.add(child);
188 }
189
190 /**
191 * Sets the actual and complete location of the node whose children have been read. This method must be called when processing
192 * the request, and the actual location must have a {@link Location#getPath() path}.
193 *
194 * @param actual the actual location of the node being read, or null if the {@link #of() current location} should be used
195 * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
196 * location} as the {@link #of() current location}, or if the actual location does not have a path.
197 */
198 public void setActualLocationOfNode( Location actual ) {
199 if (!of.isSame(actual)) { // not same if actual is null
200 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, of));
201 }
202 assert actual != null;
203 if (!actual.hasPath()) {
204 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
205 }
206 this.actualLocation = actual;
207 }
208
209 /**
210 * Get the actual location of the node whose children were read.
211 *
212 * @return the actual location, or null if the actual location was not set
213 */
214 public Location getActualLocationOfNode() {
215 return actualLocation;
216 }
217
218 /**
219 * {@inheritDoc}
220 *
221 * @see java.lang.Object#equals(java.lang.Object)
222 */
223 @Override
224 public boolean equals( Object obj ) {
225 if (this.getClass().isInstance(obj)) {
226 ReadBlockOfChildrenRequest that = (ReadBlockOfChildrenRequest)obj;
227 if (!this.of().equals(that.of())) return false;
228 if (this.startingAtIndex() != that.startingAtIndex()) return false;
229 if (this.count() != that.count()) return false;
230 return true;
231 }
232 return false;
233 }
234
235 /**
236 * {@inheritDoc}
237 *
238 * @see java.lang.Object#toString()
239 */
240 @Override
241 public String toString() {
242 Inflector inflector = Inflector.getInstance();
243 if (count() == 1) {
244 return "read " + inflector.ordinalize(startingAtIndex()) + " thru " + inflector.ordinalize(endingBefore() - 1)
245 + " children of " + of();
246 }
247 return "read " + inflector.ordinalize(startingAtIndex()) + " child of " + of();
248 }
249
250 }