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