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.util.CheckArg; 029 import org.jboss.dna.common.util.HashCode; 030 import org.jboss.dna.graph.GraphI18n; 031 import org.jboss.dna.graph.Location; 032 import org.jboss.dna.graph.connector.RepositoryConnection; 033 import org.jboss.dna.graph.property.Path; 034 import org.jboss.dna.graph.property.Property; 035 036 /** 037 * Instruction to read a block of the children of a node, where the block is dictated by the {@link #startingAfter location of the 038 * child preceding the block} and the {@link #count() maximum number of children} to include in the block. This command is useful 039 * when paging through a large number of children, when the previous block of children was already retrieved and the next block is 040 * to be read. 041 * 042 * @see ReadBlockOfChildrenRequest 043 * @author Randall Hauch 044 */ 045 public class ReadNextBlockOfChildrenRequest extends CacheableRequest { 046 047 public static final int INDEX_NOT_USED = -1; 048 049 private static final long serialVersionUID = 1L; 050 051 private final List<Location> children = new LinkedList<Location>(); 052 private final Location startingAfter; 053 private final String workspaceName; 054 private final int count; 055 private Location actualStartingAfter; 056 057 /** 058 * Create a request to read those children of a node that are immediately after a supplied sibling node. 059 * 060 * @param startingAfter the location of the previous sibling that was the last child of the previous block of children read 061 * @param workspaceName the name of the workspace containing the node 062 * @param count the maximum number of children that should be included in the block 063 * @throws IllegalArgumentException if the workspace name or <code>startingAfter</code> location is null, or if 064 * <code>count</count> is less than 1. 065 */ 066 public ReadNextBlockOfChildrenRequest( Location startingAfter, 067 String workspaceName, 068 int count ) { 069 CheckArg.isNotNull(startingAfter, "startingAfter"); 070 CheckArg.isPositive(count, "count"); 071 CheckArg.isNotNull(workspaceName, "workspaceName"); 072 this.workspaceName = workspaceName; 073 this.startingAfter = startingAfter; 074 this.count = count; 075 } 076 077 /** 078 * {@inheritDoc} 079 * 080 * @see org.jboss.dna.graph.request.Request#isReadOnly() 081 */ 082 @Override 083 public boolean isReadOnly() { 084 return true; 085 } 086 087 /** 088 * Get the maximum number of children that may be returned in the block. 089 * 090 * @return the block's maximum count 091 * @see #startingAfter() 092 */ 093 public int count() { 094 return this.count; 095 } 096 097 /** 098 * Get the location of the child after which the block begins. This form may be easier to use when paging through blocks, as 099 * the last children retrieved with the previous block can be supplied with the next read request. 100 * 101 * @return the location of the child that is immediately before the start of the block; index at which this block starts; 102 * never negative 103 * @see #count() 104 */ 105 public Location startingAfter() { 106 return this.startingAfter; 107 } 108 109 /** 110 * Get the name of the workspace in which the parent and children exist. 111 * 112 * @return the name of the workspace; never null 113 */ 114 public String inWorkspace() { 115 return workspaceName; 116 } 117 118 /** 119 * Get the children that were read from the {@link RepositoryConnection} after the request was processed. Each child is 120 * represented by a location. 121 * 122 * @return the children that were read; never null 123 */ 124 public List<Location> getChildren() { 125 return children; 126 } 127 128 /** 129 * Add to the list of children that has been read the supplied children with the given path and identification properties. The 130 * children are added in order. 131 * 132 * @param children the locations of the children that were read 133 * @throws IllegalArgumentException if the parameter is null 134 * @throws IllegalStateException if the request is frozen 135 * @see #addChild(Location) 136 * @see #addChild(Path, Property) 137 * @see #addChild(Path, Property, Property...) 138 */ 139 public void addChildren( Iterable<Location> children ) { 140 checkNotFrozen(); 141 CheckArg.isNotNull(children, "children"); 142 for (Location child : children) { 143 if (child != null) this.children.add(child); 144 } 145 } 146 147 /** 148 * Add to the list of children that has been read the child with the given path and identification properties. The children 149 * should be added in order. 150 * 151 * @param child the location of the child that was read 152 * @throws IllegalArgumentException if the location is null 153 * @throws IllegalStateException if the request is frozen 154 * @see #addChild(Path, Property) 155 * @see #addChild(Path, Property, Property...) 156 */ 157 public void addChild( Location child ) { 158 checkNotFrozen(); 159 CheckArg.isNotNull(child, "child"); 160 this.children.add(child); 161 } 162 163 /** 164 * Add to the list of children that has been read the child with the given path and identification properties. The children 165 * should be added in order. 166 * 167 * @param pathToChild the path of the child that was just read 168 * @param firstIdProperty the first identification property of the child that was just read 169 * @param remainingIdProperties the remaining identification properties of the child that was just read 170 * @throws IllegalArgumentException if the path or identification properties are null 171 * @throws IllegalStateException if the request is frozen 172 * @see #addChild(Location) 173 * @see #addChild(Path, Property) 174 */ 175 public void addChild( Path pathToChild, 176 Property firstIdProperty, 177 Property... remainingIdProperties ) { 178 checkNotFrozen(); 179 Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties); 180 this.children.add(child); 181 } 182 183 /** 184 * Add to the list of children that has been read the child with the given path and identification property. The children 185 * should be added in order. 186 * 187 * @param pathToChild the path of the child that was just read 188 * @param idProperty the identification property of the child that was just read 189 * @throws IllegalArgumentException if the path or identification properties are null 190 * @throws IllegalStateException if the request is frozen 191 * @see #addChild(Location) 192 * @see #addChild(Path, Property, Property...) 193 */ 194 public void addChild( Path pathToChild, 195 Property idProperty ) { 196 checkNotFrozen(); 197 Location child = Location.create(pathToChild, idProperty); 198 this.children.add(child); 199 } 200 201 /** 202 * Sets the actual and complete location of the node whose children have been read. This method must be called when processing 203 * the request, and the actual location must have a {@link Location#getPath() path}. 204 * 205 * @param actual the actual location of the node being read, or null if the {@link #startingAfter() starting after location} 206 * should be used 207 * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same 208 * location} as the {@link #startingAfter() starting after location}, or if the actual location does not have a path. 209 * @throws IllegalStateException if the request is frozen 210 */ 211 public void setActualLocationOfStartingAfterNode( Location actual ) { 212 checkNotFrozen(); 213 if (!startingAfter.isSame(actual)) { // not same if actual is null 214 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, startingAfter)); 215 } 216 assert actual != null; 217 if (!actual.hasPath()) { 218 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual)); 219 } 220 this.actualStartingAfter = actual; 221 } 222 223 /** 224 * Get the actual location of the {@link #startingAfter() starting after} sibling. 225 * 226 * @return the actual location, or null if the actual location was not set 227 */ 228 public Location getActualLocationOfStartingAfterNode() { 229 return actualStartingAfter; 230 } 231 232 /** 233 * {@inheritDoc} 234 * 235 * @see org.jboss.dna.graph.request.Request#cancel() 236 */ 237 @Override 238 public void cancel() { 239 super.cancel(); 240 this.actualStartingAfter = null; 241 this.children.clear(); 242 } 243 244 /** 245 * {@inheritDoc} 246 * 247 * @see java.lang.Object#hashCode() 248 */ 249 @Override 250 public int hashCode() { 251 return HashCode.compute(startingAfter, workspaceName); 252 } 253 254 /** 255 * {@inheritDoc} 256 * 257 * @see java.lang.Object#equals(java.lang.Object) 258 */ 259 @Override 260 public boolean equals( Object obj ) { 261 if (obj == this) return true; 262 if (this.getClass().isInstance(obj)) { 263 ReadNextBlockOfChildrenRequest that = (ReadNextBlockOfChildrenRequest)obj; 264 if (!this.startingAfter().equals(that.startingAfter())) return false; 265 if (this.count() != that.count()) return false; 266 if (!this.inWorkspace().equals(that.inWorkspace())) return false; 267 return true; 268 } 269 return false; 270 } 271 272 /** 273 * {@inheritDoc} 274 * 275 * @see java.lang.Object#toString() 276 */ 277 @Override 278 public String toString() { 279 if (count() == 1) { 280 return "read the next child after " + startingAfter() + " in the \"" + workspaceName + "\" workspace"; 281 } 282 return "read the next " + count() + " children after " + startingAfter() + " in the \"" + workspaceName + "\" workspace"; 283 } 284 285 }