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 }