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 }