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 }