View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.request;
25  
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.Iterator;
29  import java.util.LinkedList;
30  import java.util.List;
31  import java.util.concurrent.atomic.AtomicBoolean;
32  import net.jcip.annotations.Immutable;
33  import org.modeshape.common.util.CheckArg;
34  
35  /**
36   * A request that wraps multiple other requests, allowing multiple requests to be treated as a single request.
37   * <p>
38   * Note that {@link #isCancelled()} and {@link #cancel()} apply to all requests contained by the composite request. In other
39   * words, cancelling this request immediately marks all contained requests as cancelled. However, cancelling any request in the
40   * request has the effect of cancelling all other requests in the composite, including the composite. (This is implemented by
41   * having all {@link Request} objects in the composite share the same cancelled flag object.)
42   * </p>
43   */
44  @Immutable
45  public class CompositeRequest extends Request implements Iterable<Request> {
46  
47      private static final long serialVersionUID = 1L;
48  
49      public static final int UNKNOWN_NUMBER_OF_REQUESTS = Integer.MAX_VALUE;
50  
51      /**
52       * Return a request that either wraps multiple requests, or the single request if only one is supplied.
53       * 
54       * @param requests the requests to wrap
55       * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request
56       * @throws IllegalArgumentException if there requests are null, empty, or contains only nulls
57       */
58      public static Request with( Request... requests ) {
59          CheckArg.isNotEmpty(requests, "requests");
60          if (requests.length == 1) {
61              CheckArg.isNotNull(requests[0], "requests[0]");
62              return requests[0];
63          }
64          boolean readOnly = true;
65          List<Request> list = new ArrayList<Request>(requests.length);
66          for (Request request : requests) {
67              if (request == null) continue;
68              if (RequestType.COMPOSITE == request.getType()) {
69                  CompositeRequest composite = (CompositeRequest)request;
70                  list.addAll(composite.getRequests());
71                  if (!composite.isReadOnly()) readOnly = false;
72              } else {
73                  list.add(request);
74                  if (!request.isReadOnly()) readOnly = false;
75              }
76          }
77          CheckArg.isNotEmpty(list, "requests");
78          return new CompositeRequest(list, readOnly);
79      }
80  
81      /**
82       * Return a request that either wraps multiple requests, or the single request if only one is supplied.
83       * 
84       * @param requests the requests to wrap
85       * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request
86       * @throws IllegalArgumentException if there requests are null, empty, or contains only nulls
87       */
88      public static Request with( Iterator<? extends Request> requests ) {
89          CheckArg.isNotNull(requests, "requests");
90          boolean readOnly = true;
91          List<Request> list = new LinkedList<Request>();
92          while (requests.hasNext()) {
93              Request request = requests.next();
94              if (request == null) continue;
95              if (RequestType.COMPOSITE == request.getType()) {
96                  CompositeRequest composite = (CompositeRequest)request;
97                  list.addAll(composite.getRequests());
98                  if (!composite.isReadOnly()) readOnly = false;
99              } else {
100                 list.add(request);
101                 if (!request.isReadOnly()) readOnly = false;
102             }
103         }
104         if (list.size() == 1) {
105             return list.get(0);
106         }
107         CheckArg.isNotEmpty(list, "requests");
108         return new CompositeRequest(list, readOnly);
109     }
110 
111     /**
112      * Return a request that either wraps multiple requests, or the single request if only one is supplied.
113      * 
114      * @param requests the requests to wrap
115      * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request
116      * @throws IllegalArgumentException if there requests are null or empty
117      */
118     public static Request with( List<? extends Request> requests ) {
119         CheckArg.isNotEmpty(requests, "requests");
120         if (requests.size() == 1) {
121             return requests.get(0);
122         }
123         boolean readOnly = true;
124         for (Request request : requests) {
125             readOnly = request.isReadOnly();
126             if (!readOnly) break;
127         }
128         return new CompositeRequest(requests, readOnly);
129     }
130 
131     /**
132      * Add requests to the supplied composite request.
133      * 
134      * @param composite the composite request to which the requests are to be added
135      * @param requests the requests to wrap
136      * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request, or null if
137      *         there are no request
138      * @throws IllegalArgumentException if the composite request is null
139      */
140     public static CompositeRequest add( CompositeRequest composite,
141                                         Request... requests ) {
142         CheckArg.isNotNull(composite, "composite");
143         if (requests == null || requests.length == 0) return composite;
144         List<Request> list = new ArrayList<Request>(requests.length + composite.size());
145         boolean readOnly = composite.isReadOnly();
146         if (composite.size() != 0) list.addAll(composite.getRequests());
147         for (Request request : requests) {
148             if (request == null) continue;
149             if (RequestType.COMPOSITE == request.getType()) {
150                 CompositeRequest compositeRequest = (CompositeRequest)request;
151                 list.addAll(compositeRequest.getRequests());
152                 if (!compositeRequest.isReadOnly()) readOnly = false;
153             } else {
154                 list.add(request);
155                 if (!request.isReadOnly()) readOnly = false;
156             }
157         }
158         return new CompositeRequest(list, readOnly);
159     }
160 
161     /**
162      * Add requests to the supplied composite request.
163      * 
164      * @param composite the composite request to which the requests are to be added
165      * @param requests the requests to wrap
166      * @return the requests wrapped in a CompositeRequest, or if only one request is supplied that single request, or null if
167      *         there are no request
168      * @throws IllegalArgumentException if the composite request is null
169      */
170     public static CompositeRequest add( CompositeRequest composite,
171                                         Iterator<? extends Request> requests ) {
172         CheckArg.isNotNull(composite, "composite");
173         List<Request> list = new LinkedList<Request>();
174         boolean readOnly = composite.isReadOnly();
175         if (composite.size() != 0) list.addAll(composite.getRequests());
176         while (requests.hasNext()) {
177             Request request = requests.next();
178             if (request == null) continue;
179             if (RequestType.COMPOSITE == request.getType()) {
180                 CompositeRequest compositeRequest = (CompositeRequest)request;
181                 list.addAll(compositeRequest.getRequests());
182                 if (!compositeRequest.isReadOnly()) readOnly = false;
183             } else {
184                 list.add(request);
185                 if (!request.isReadOnly()) readOnly = false;
186             }
187         }
188         return new CompositeRequest(list, readOnly);
189     }
190 
191     private final List<Request> requests;
192     private final boolean readOnly;
193 
194     /**
195      * Create a composite request from the supplied list of requests.
196      * 
197      * @param requests the modifiable list of requests; may not be null
198      * @param readOnly true if all of the requests are {@link Request#isReadOnly() read-only}
199      */
200     protected CompositeRequest( List<? extends Request> requests,
201                                 boolean readOnly ) {
202         // Iterate through the requests and set the cancelled flag of each request to this object's flag ...
203         final AtomicBoolean flag = super.getCancelledFlag();
204         for (Request request : requests) {
205             request.setCancelledFlag(flag);
206         }
207         this.requests = Collections.unmodifiableList(requests);
208         this.readOnly = readOnly;
209     }
210 
211     /**
212      * Create a composite request from the supplied list of requests. This is useful only for subclasses.
213      * 
214      * @param readOnly true if all of the requests are {@link Request#isReadOnly() read-only}
215      */
216     protected CompositeRequest( boolean readOnly ) {
217         this.requests = Collections.emptyList();
218         this.readOnly = readOnly;
219     }
220 
221     /**
222      * Return the unmodifiable requests contained in this composite request.
223      * 
224      * @return requests
225      */
226     public List<Request> getRequests() {
227         return requests;
228     }
229 
230     /**
231      * Get the number of requests.
232      * 
233      * @return the number of requests
234      */
235     public int size() {
236         return requests.size();
237     }
238 
239     /**
240      * {@inheritDoc}
241      * 
242      * @see java.lang.Iterable#iterator()
243      */
244     public Iterator<Request> iterator() {
245         return requests.iterator();
246     }
247 
248     /**
249      * {@inheritDoc}
250      * 
251      * @see org.modeshape.graph.request.Request#isReadOnly()
252      */
253     @Override
254     public boolean isReadOnly() {
255         return readOnly;
256     }
257 
258     /**
259      * Check the set of requests for errors. Calling this method will always result in the {@link #getError() existing error}
260      * being reset to a new error or null if there are no errors.
261      */
262     public void checkForErrors() {
263         Request firstRequestWithError = null;
264         LinkedList<Request> requestsWithErrors = null;
265         for (Request request : requests) {
266             if (request.hasError()) {
267                 if (firstRequestWithError == null) {
268                     firstRequestWithError = request;
269                 } else {
270                     // This is not the first ...
271                     if (requestsWithErrors == null) {
272                         requestsWithErrors = new LinkedList<Request>();
273                         requestsWithErrors.add(firstRequestWithError);
274                     }
275                     requestsWithErrors.add(request);
276                 }
277             }
278         }
279         if (firstRequestWithError != null) {
280             // There is at least one error ...
281             if (requestsWithErrors == null) {
282                 // but only one ...
283                 setError(firstRequestWithError.getError());
284             } else {
285                 // More than one error ...
286                 setError(new MultipleRequestFailuresException(requestsWithErrors, size()));
287             }
288         }
289     }
290 
291     /**
292      * {@inheritDoc}
293      * 
294      * @see java.lang.Object#equals(java.lang.Object)
295      */
296     @Override
297     public boolean equals( Object obj ) {
298         if (obj == this) return true;
299         if (obj instanceof CompositeRequest) {
300             CompositeRequest that = (CompositeRequest)obj;
301             if (this.size() != that.size()) return false;
302             Iterator<Request> thisIter = this.iterator();
303             Iterator<Request> thatIter = that.iterator();
304             while (thisIter.hasNext()) {
305                 Request thisRequest = thisIter.next();
306                 Request thatRequest = thatIter.next();
307                 if (thisRequest == null) {
308                     if (thatRequest != null) return false;
309                 } else {
310                     if (!thisRequest.equals(thatRequest)) return false;
311                 }
312             }
313             return true;
314         }
315         return false;
316     }
317 
318     @Override
319     public String toString() {
320         StringBuffer buff = new StringBuffer();
321         buff.append("[CompositeRequest (" + size() + ")");
322 
323         for (Request r : this.getRequests()) {
324             buff.append("\n\t").append(r);
325         }
326 
327         buff.append("]");
328         return buff.toString();
329     }
330 
331     @Override
332     public RequestType getType() {
333         return RequestType.COMPOSITE;
334     }
335 }