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