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