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.ArrayList; 027 import java.util.Collections; 028 import java.util.Iterator; 029 import java.util.LinkedList; 030 import java.util.List; 031 import java.util.concurrent.atomic.AtomicBoolean; 032 import net.jcip.annotations.Immutable; 033 import org.jboss.dna.common.util.CheckArg; 034 035 /** 036 * A request that wraps multiple other requests, allowing multiple requests to be treated as a single request. 037 * <p> 038 * Note that {@link #isCancelled()} and {@link #cancel()} apply to all requests contained by the composite request. In other 039 * words, cancelling this request immediately marks all contained requests as cancelled. However, cancelling any request in the 040 * request has the effect of cancelling all other requests in the composite, including the composite. (This is implemented by 041 * having all {@link Request} objects in the composite share the same cancelled flag object.) 042 * </p> 043 * 044 * @author Randall Hauch 045 */ 046 @Immutable 047 public class CompositeRequest extends Request implements Iterable<Request> { 048 049 private static final long serialVersionUID = 1L; 050 051 public static final int UNKNOWN_NUMBER_OF_REQUESTS = Integer.MAX_VALUE; 052 053 /** 054 * Return a request that either wraps multiple requests, or the single request if only one is supplied. 055 * 056 * @param requests the requests to wrap 057 * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request 058 * @throws IllegalArgumentException if there requests are null, empty, or contains only nulls 059 */ 060 public static Request with( Request... requests ) { 061 CheckArg.isNotEmpty(requests, "requests"); 062 if (requests.length == 1) { 063 CheckArg.isNotNull(requests[0], "requests[0]"); 064 return requests[0]; 065 } 066 boolean readOnly = true; 067 List<Request> list = new ArrayList<Request>(requests.length); 068 for (Request request : requests) { 069 if (request == null) continue; 070 if (request instanceof CompositeRequest) { 071 CompositeRequest composite = (CompositeRequest)request; 072 list.addAll(composite.getRequests()); 073 if (!composite.isReadOnly()) readOnly = false; 074 } else { 075 list.add(request); 076 if (!request.isReadOnly()) readOnly = false; 077 } 078 } 079 CheckArg.isNotEmpty(list, "requests"); 080 return new CompositeRequest(list, readOnly); 081 } 082 083 /** 084 * Return a request that either wraps multiple requests, or the single request if only one is supplied. 085 * 086 * @param requests the requests to wrap 087 * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request 088 * @throws IllegalArgumentException if there requests are null, empty, or contains only nulls 089 */ 090 public static Request with( Iterator<? extends Request> requests ) { 091 CheckArg.isNotNull(requests, "requests"); 092 boolean readOnly = true; 093 List<Request> list = new LinkedList<Request>(); 094 while (requests.hasNext()) { 095 Request request = requests.next(); 096 if (request == null) continue; 097 if (request instanceof CompositeRequest) { 098 CompositeRequest composite = (CompositeRequest)request; 099 list.addAll(composite.getRequests()); 100 if (!composite.isReadOnly()) readOnly = false; 101 } else { 102 list.add(request); 103 if (!request.isReadOnly()) readOnly = false; 104 } 105 } 106 if (list.size() == 1) { 107 return list.get(0); 108 } 109 CheckArg.isNotEmpty(list, "requests"); 110 return new CompositeRequest(list, readOnly); 111 } 112 113 /** 114 * Return a request that either wraps multiple requests, or the single request if only one is supplied. 115 * 116 * @param requests the requests to wrap 117 * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request 118 * @throws IllegalArgumentException if there requests are null or empty 119 */ 120 public static Request with( List<? extends Request> requests ) { 121 CheckArg.isNotEmpty(requests, "requests"); 122 if (requests.size() == 1) { 123 return requests.get(0); 124 } 125 boolean readOnly = true; 126 for (Request request : requests) { 127 readOnly = request.isReadOnly(); 128 if (!readOnly) break; 129 } 130 return new CompositeRequest(requests, readOnly); 131 } 132 133 /** 134 * Add requests to the supplied composite request. 135 * 136 * @param composite the composite request to which the requests are to be added 137 * @param requests the requests to wrap 138 * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request, or null if 139 * there are no request 140 * @throws IllegalArgumentException if the composite request is null 141 */ 142 public static CompositeRequest add( CompositeRequest composite, 143 Request... requests ) { 144 CheckArg.isNotNull(composite, "composite"); 145 if (requests == null || requests.length == 0) return composite; 146 List<Request> list = new ArrayList<Request>(requests.length + composite.size()); 147 boolean readOnly = composite.isReadOnly(); 148 if (composite.size() != 0) list.addAll(composite.getRequests()); 149 for (Request request : requests) { 150 if (request == null) continue; 151 if (request instanceof CompositeRequest) { 152 CompositeRequest compositeRequest = (CompositeRequest)request; 153 list.addAll(compositeRequest.getRequests()); 154 if (!compositeRequest.isReadOnly()) readOnly = false; 155 } else { 156 list.add(request); 157 if (!request.isReadOnly()) readOnly = false; 158 } 159 } 160 return new CompositeRequest(list, readOnly); 161 } 162 163 /** 164 * Add requests to the supplied composite request. 165 * 166 * @param composite the composite request to which the requests are to be added 167 * @param requests the requests to wrap 168 * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request, or null if 169 * there are no request 170 * @throws IllegalArgumentException if the composite request is null 171 */ 172 public static CompositeRequest add( CompositeRequest composite, 173 Iterator<? extends Request> requests ) { 174 CheckArg.isNotNull(composite, "composite"); 175 List<Request> list = new LinkedList<Request>(); 176 boolean readOnly = composite.isReadOnly(); 177 if (composite.size() != 0) list.addAll(composite.getRequests()); 178 while (requests.hasNext()) { 179 Request request = requests.next(); 180 if (request == null) continue; 181 if (request instanceof CompositeRequest) { 182 CompositeRequest compositeRequest = (CompositeRequest)request; 183 list.addAll(compositeRequest.getRequests()); 184 if (!compositeRequest.isReadOnly()) readOnly = false; 185 } else { 186 list.add(request); 187 if (!request.isReadOnly()) readOnly = false; 188 } 189 } 190 return new CompositeRequest(list, readOnly); 191 } 192 193 private final List<Request> requests; 194 private final boolean readOnly; 195 196 /** 197 * Create a composite request from the supplied list of requests. 198 * 199 * @param requests the modifiable list of requests; may not be null 200 * @param readOnly true if all of the requests are {@link Request#isReadOnly() read-only} 201 */ 202 protected CompositeRequest( List<? extends Request> requests, 203 boolean readOnly ) { 204 // Iterate through the requests and set the cancelled flag of each request to this object's flag ... 205 final AtomicBoolean flag = super.getCancelledFlag(); 206 for (Request request : requests) { 207 request.setCancelledFlag(flag); 208 } 209 this.requests = Collections.unmodifiableList(requests); 210 this.readOnly = readOnly; 211 } 212 213 /** 214 * Create a composite request from the supplied list of requests. This is useful only for subclasses. 215 * 216 * @param readOnly true if all of the requests are {@link Request#isReadOnly() read-only} 217 */ 218 protected CompositeRequest( boolean readOnly ) { 219 this.requests = Collections.emptyList(); 220 this.readOnly = false; 221 } 222 223 /** 224 * Return the unmodifiable requests contained in this composite request. 225 * 226 * @return requests 227 */ 228 public List<Request> getRequests() { 229 return requests; 230 } 231 232 /** 233 * Get the number of requests. 234 * 235 * @return the number of requests 236 */ 237 public int size() { 238 return requests.size(); 239 } 240 241 /** 242 * {@inheritDoc} 243 * 244 * @see java.lang.Iterable#iterator() 245 */ 246 public Iterator<Request> iterator() { 247 return requests.iterator(); 248 } 249 250 /** 251 * {@inheritDoc} 252 * 253 * @see org.jboss.dna.graph.request.Request#isReadOnly() 254 */ 255 @Override 256 public boolean isReadOnly() { 257 return readOnly; 258 } 259 260 /** 261 * {@inheritDoc} 262 * 263 * @see java.lang.Object#equals(java.lang.Object) 264 */ 265 @Override 266 public boolean equals( Object obj ) { 267 if (obj == this) return true; 268 if (obj instanceof CompositeRequest) { 269 CompositeRequest that = (CompositeRequest)obj; 270 if (this.size() != that.size()) return false; 271 Iterator<Request> thisIter = this.iterator(); 272 Iterator<Request> thatIter = that.iterator(); 273 while (thisIter.hasNext()) { 274 Request thisRequest = thisIter.next(); 275 Request thatRequest = thatIter.next(); 276 if (thisRequest == null) { 277 if (thatRequest != null) return false; 278 } else { 279 if (!thisRequest.equals(thatRequest)) return false; 280 } 281 } 282 return true; 283 } 284 return false; 285 } 286 287 }