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 }