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.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 #startingAtIndex() starting 038 * index} and the {@link #count() maximum number of children} to include in the block. This command is useful when paging through 039 * a large number of children. 040 * 041 * @see ReadNextBlockOfChildrenRequest 042 * @author Randall Hauch 043 */ 044 public class ReadBlockOfChildrenRequest extends CacheableRequest { 045 046 public static final int INDEX_NOT_USED = -1; 047 048 private static final long serialVersionUID = 1L; 049 050 private final Location of; 051 private final String workspaceName; 052 private final List<Location> children = new LinkedList<Location>(); 053 private final int startingAtIndex; 054 private final int count; 055 private Location actualLocation; 056 057 /** 058 * Create a request to read a block of the children of a node at the supplied location. The block is defined by the starting 059 * index of the first child and the number of children to include. Note that this index is <i>not</i> the 060 * {@link Path.Segment#getIndex() same-name-sibiling index}, but rather is the index of the child as if the children were in 061 * an array. 062 * 063 * @param of the location of the node whose children are to be read 064 * @param workspaceName the name of the workspace containing the parent 065 * @param startingIndex the zero-based index of the first child to be included in the block 066 * @param count the maximum number of children that should be included in the block 067 * @throws IllegalArgumentException if the location or workspace name is null, if <code>startingIndex</code> is negative, or 068 * if <code>count</count> is less than 1. 069 */ 070 public ReadBlockOfChildrenRequest( Location of, 071 String workspaceName, 072 int startingIndex, 073 int count ) { 074 CheckArg.isNotNull(of, "of"); 075 CheckArg.isNonNegative(startingIndex, "startingIndex"); 076 CheckArg.isPositive(count, "count"); 077 CheckArg.isNotNull(workspaceName, "workspaceName"); 078 this.workspaceName = workspaceName; 079 this.of = of; 080 this.startingAtIndex = startingIndex; 081 this.count = count; 082 } 083 084 /** 085 * {@inheritDoc} 086 * 087 * @see org.jboss.dna.graph.request.Request#isReadOnly() 088 */ 089 @Override 090 public boolean isReadOnly() { 091 return true; 092 } 093 094 /** 095 * Get the location defining the node whose children are to be read. 096 * 097 * @return the location of the parent node; never null 098 */ 099 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 child with the given path and identification properties. The children 161 * should be added in order. 162 * 163 * @param child the location of the child that was read 164 * @throws IllegalArgumentException if the location is null 165 * @see #addChild(Path, Property) 166 * @see #addChild(Path, Property, Property...) 167 */ 168 public void addChild( Location child ) { 169 CheckArg.isNotNull(child, "child"); 170 this.children.add(child); 171 } 172 173 /** 174 * Add to the list of children that has been read the child with the given path and identification properties. The children 175 * should be added in order. 176 * 177 * @param pathToChild the path of the child that was just read 178 * @param firstIdProperty the first identification property of the child that was just read 179 * @param remainingIdProperties the remaining identification properties 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) 183 */ 184 public void addChild( Path pathToChild, 185 Property firstIdProperty, 186 Property... remainingIdProperties ) { 187 Location child = Location.create(pathToChild, firstIdProperty, remainingIdProperties); 188 this.children.add(child); 189 } 190 191 /** 192 * Add to the list of children that has been read the child with the given path and identification property. The children 193 * should be added in order. 194 * 195 * @param pathToChild the path of the child that was just read 196 * @param idProperty the identification property of the child that was just read 197 * @throws IllegalArgumentException if the path or identification properties are null 198 * @see #addChild(Location) 199 * @see #addChild(Path, Property, Property...) 200 */ 201 public void addChild( Path pathToChild, 202 Property idProperty ) { 203 Location child = Location.create(pathToChild, idProperty); 204 this.children.add(child); 205 } 206 207 /** 208 * Sets the actual and complete location of the node whose children have been read. This method must be called when processing 209 * the request, and the actual location must have a {@link Location#getPath() path}. 210 * 211 * @param actual the actual location of the node being read, or null if the {@link #of() current location} should be used 212 * @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same 213 * location} as the {@link #of() current location}, or if the actual location does not have a path. 214 */ 215 public void setActualLocationOfNode( Location actual ) { 216 if (!of.isSame(actual)) { // not same if actual is null 217 throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, of)); 218 } 219 assert actual != null; 220 if (!actual.hasPath()) { 221 throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual)); 222 } 223 this.actualLocation = actual; 224 } 225 226 /** 227 * Get the actual location of the node whose children were read. 228 * 229 * @return the actual location, or null if the actual location was not set 230 */ 231 public Location getActualLocationOfNode() { 232 return actualLocation; 233 } 234 235 /** 236 * {@inheritDoc} 237 * 238 * @see java.lang.Object#equals(java.lang.Object) 239 */ 240 @Override 241 public boolean equals( Object obj ) { 242 if (obj == this) return true; 243 if (this.getClass().isInstance(obj)) { 244 ReadBlockOfChildrenRequest that = (ReadBlockOfChildrenRequest)obj; 245 if (!this.of().equals(that.of())) return false; 246 if (this.startingAtIndex() != that.startingAtIndex()) return false; 247 if (this.count() != that.count()) return false; 248 if (!this.inWorkspace().equals(that.inWorkspace())) return false; 249 return true; 250 } 251 return false; 252 } 253 254 /** 255 * {@inheritDoc} 256 * 257 * @see java.lang.Object#toString() 258 */ 259 @Override 260 public String toString() { 261 Inflector inflector = Inflector.getInstance(); 262 if (count() == 1) { 263 return "read " + inflector.ordinalize(startingAtIndex()) + " thru " + inflector.ordinalize(endingBefore() - 1) 264 + " children of " + of() + " in the \"" + workspaceName + "\" workspace"; 265 } 266 return "read " + inflector.ordinalize(startingAtIndex()) + " child of " + of() + " in the \"" + workspaceName 267 + "\" workspace"; 268 } 269 270 }