View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions Copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2012-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.resource;
18  
19  import static org.forgerock.util.Utils.*;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.LinkedHashMap;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.forgerock.http.routing.Version;
30  import org.forgerock.json.JsonException;
31  import org.forgerock.json.JsonPointer;
32  import org.forgerock.json.JsonValue;
33  import org.forgerock.util.i18n.PreferredLocales;
34  
35  /**
36   * A utility class containing various factory methods for creating and
37   * manipulating requests.
38   */
39  public final class Requests {
40      private static abstract class AbstractRequestImpl<T extends Request> implements Request {
41          private final List<JsonPointer> fields = new LinkedList<>();
42          private ResourcePath resourcePath;
43          private final Map<String, String> parameters = new LinkedHashMap<>(2);
44          private Version resourceVersion;
45          private PreferredLocales preferredLocales;
46  
47          protected AbstractRequestImpl() {
48              // Default constructor.
49          }
50  
51          protected AbstractRequestImpl(final Request request) {
52              this.resourcePath = request.getResourcePathObject();
53              this.fields.addAll(request.getFields());
54              this.parameters.putAll(request.getAdditionalParameters());
55              this.resourceVersion = request.getResourceVersion();
56              this.preferredLocales = request.getPreferredLocales();
57          }
58  
59          @Override
60          public final T addField(final JsonPointer... fields) {
61              for (final JsonPointer field : fields) {
62                  this.fields.add(notNull(field));
63              }
64              return getThis();
65          }
66  
67          @Override
68          public final T addField(final String... fields) {
69              try {
70                  for (final String field : fields) {
71                      this.fields.add(new JsonPointer(field));
72                  }
73              } catch (final JsonException e) {
74                  throw new IllegalArgumentException(e.getMessage());
75              }
76              return getThis();
77          }
78  
79          @Override
80          public final List<JsonPointer> getFields() {
81              return fields;
82          }
83  
84          @Override
85          public final String getResourcePath() {
86              return resourcePath.toString();
87          }
88  
89          @Override
90          public final ResourcePath getResourcePathObject() {
91              return resourcePath;
92          }
93  
94          @Override
95          public Map<String, String> getAdditionalParameters() {
96              return parameters;
97          }
98  
99          @Override
100         public String getAdditionalParameter(final String name) {
101             return parameters.get(name);
102         }
103 
104         @Override
105         public Version getResourceVersion() {
106             return resourceVersion;
107         }
108 
109         @Override
110         public final T setResourcePath(final String path) {
111             resourcePath = ResourcePath.valueOf(path);
112             return getThis();
113         }
114 
115         @Override
116         public final T setResourcePath(final ResourcePath path) {
117             resourcePath = notNull(path);
118             return getThis();
119         }
120 
121         @Override
122         public T setAdditionalParameter(final String name, final String value) throws BadRequestException {
123             if (isReservedParameter(name)) {
124                 throw new BadRequestException("Unrecognized request parameter '" + name + "'");
125             }
126             parameters.put(notNull(name), value);
127             return getThis();
128         }
129 
130         @Override
131         public T setResourceVersion(Version resourceVersion) {
132             this.resourceVersion = resourceVersion;
133             return getThis();
134         }
135 
136         boolean isReservedParameter(String name) {
137             return name.startsWith("_");
138         }
139 
140         protected abstract T getThis();
141 
142         @Override
143         public JsonValue toJsonValue() {
144             return new JsonValue(new HashMap<>())
145                     .put("method", getRequestType().name().toLowerCase())
146                     .put(FIELD_RESOURCE_PATH, getResourcePath())
147                     .put(FIELD_FIELDS, getFields());
148         }
149 
150         @Override
151         public String toString() {
152             return toJsonValue().toString();
153         }
154 
155         @Override
156         public PreferredLocales getPreferredLocales() {
157             return preferredLocales;
158         }
159 
160         @Override
161         public T setPreferredLocales(PreferredLocales preferredLocales) {
162             this.preferredLocales = preferredLocales;
163             return getThis();
164         }
165     }
166 
167     private static final class ActionRequestImpl extends AbstractRequestImpl<ActionRequest>
168             implements ActionRequest {
169         private String actionId;
170         private JsonValue content;
171 
172         private ActionRequestImpl() {
173             // Default constructor.
174             content = new JsonValue(null);
175         }
176 
177         private ActionRequestImpl(final ActionRequest request) {
178             super(request);
179             this.actionId = request.getAction();
180             this.content = copyJsonValue(request.getContent());
181         }
182 
183         @Override
184         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
185             return v.visitActionRequest(p, this);
186         }
187 
188         @Override
189         public String getAction() {
190             return actionId;
191         }
192 
193         @Override
194         public <T extends Enum<T>> T getActionAsEnum(final Class<T> type) {
195             return asEnum(getAction(), type);
196         }
197 
198         @Override
199         public JsonValue getContent() {
200             return content;
201         }
202 
203         @Override
204         public ActionRequest setAction(final String id) {
205             this.actionId = notNull(id);
206             return this;
207         }
208 
209         @Override
210         public ActionRequest setContent(final JsonValue content) {
211             this.content = content != null ? content : new JsonValue(null);
212             return this;
213         }
214 
215         @Override
216         protected ActionRequest getThis() {
217             return this;
218         }
219 
220         @Override
221         public RequestType getRequestType() {
222             return RequestType.ACTION;
223         }
224 
225         @Override
226         public JsonValue toJsonValue() {
227             return super.toJsonValue()
228                     .put(FIELD_ACTION, String.valueOf(getAction()))
229                     .put(FIELD_CONTENT, getContent().getObject())
230                     .put(FIELD_ADDITIONAL_PARAMETERS, getAdditionalParameters());
231         }
232 
233         @Override
234         boolean isReservedParameter(String name) {
235             // no reserved parameters for ActionRequests in order to support current patch-by-query usage *except*
236             // mimeType which only applies to true read requests.
237             return name.equals("_mimeType");
238         }
239     }
240 
241     private static final class CreateRequestImpl extends AbstractRequestImpl<CreateRequest>
242             implements CreateRequest {
243         private JsonValue content;
244         private String newResourceId;
245 
246         private CreateRequestImpl() {
247             // Default constructor.
248         }
249 
250         private CreateRequestImpl(final CreateRequest request) {
251             super(request);
252             this.content = copyJsonValue(request.getContent());
253             this.newResourceId = request.getNewResourceId();
254         }
255 
256         @Override
257         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
258             return v.visitCreateRequest(p, this);
259         }
260 
261         @Override
262         public JsonValue getContent() {
263             return content;
264         }
265 
266         @Override
267         public String getNewResourceId() {
268             return newResourceId;
269         }
270 
271         @Override
272         public CreateRequest setContent(final JsonValue content) {
273             this.content = notNull(content);
274             return this;
275         }
276 
277         @Override
278         public CreateRequest setNewResourceId(final String id) {
279             this.newResourceId = id;
280             return this;
281         }
282 
283         @Override
284         protected CreateRequest getThis() {
285             return this;
286         }
287 
288         @Override
289         public RequestType getRequestType() {
290             return RequestType.CREATE;
291         }
292 
293         @Override
294         public JsonValue toJsonValue() {
295             return super.toJsonValue()
296                     .put(FIELD_NEW_RESOURCE_ID, getNewResourceId())
297                     .put(FIELD_CONTENT, getContent().getObject());
298         }
299 
300     }
301 
302     private static final class DeleteRequestImpl extends AbstractRequestImpl<DeleteRequest>
303             implements DeleteRequest {
304         private String version;
305 
306         private DeleteRequestImpl() {
307             // Default constructor.
308         }
309 
310         private DeleteRequestImpl(final DeleteRequest request) {
311             super(request);
312             this.version = request.getRevision();
313         }
314 
315         @Override
316         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
317             return v.visitDeleteRequest(p, this);
318         }
319 
320         @Override
321         public String getRevision() {
322             return version;
323         }
324 
325         @Override
326         public DeleteRequest setRevision(final String version) {
327             this.version = version;
328             return this;
329         }
330 
331         @Override
332         protected DeleteRequest getThis() {
333             return this;
334         }
335 
336         @Override
337         public RequestType getRequestType() {
338             return RequestType.DELETE;
339         }
340 
341         @Override
342         public JsonValue toJsonValue() {
343             return super.toJsonValue()
344                     .put(FIELD_REVISION, String.valueOf(getRevision()));
345         }
346 
347     }
348 
349     private static final class PatchRequestImpl extends AbstractRequestImpl<PatchRequest> implements
350             PatchRequest {
351         private List<PatchOperation> operations;
352         private String version;
353 
354         private PatchRequestImpl() {
355             operations = new LinkedList<>();
356         }
357 
358         private PatchRequestImpl(final PatchRequest request) {
359             super(request);
360             this.operations = new LinkedList<>(request.getPatchOperations());
361             this.version = request.getRevision();
362         }
363 
364         @Override
365         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
366             return v.visitPatchRequest(p, this);
367         }
368 
369         @Override
370         public String getRevision() {
371             return version;
372         }
373 
374         @Override
375         public PatchRequest addPatchOperation(PatchOperation... operations) {
376             Collections.addAll(this.operations, operations);
377             return this;
378         }
379 
380         @Override
381         public List<PatchOperation> getPatchOperations() {
382             return operations;
383         }
384 
385         @Override
386         public PatchRequest addPatchOperation(String operation, String field, JsonValue value) {
387             operations.add(PatchOperation.operation(operation, field, value));
388             return this;
389         }
390 
391         @Override
392         public PatchRequest setRevision(final String version) {
393             this.version = version;
394             return this;
395         }
396 
397         @Override
398         protected PatchRequest getThis() {
399             return this;
400         }
401 
402         @Override
403         public RequestType getRequestType() {
404             return RequestType.PATCH;
405         }
406 
407         @Override
408         public JsonValue toJsonValue() {
409             final List<Object> operations = new ArrayList<>();
410             for (PatchOperation operation : getPatchOperations()) {
411                 operations.add(operation.toJsonValue().getObject());
412             }
413             return super.toJsonValue()
414                     .put(FIELD_REVISION, String.valueOf(getRevision()))
415                     .put(FIELD_PATCH_OPERATIONS, operations);
416         }
417     }
418 
419     private static final class QueryRequestImpl extends AbstractRequestImpl<QueryRequest> implements
420             QueryRequest {
421         private org.forgerock.util.query.QueryFilter<JsonPointer> filter;
422         private final List<SortKey> keys = new LinkedList<>();
423         private String pagedResultsCookie;
424         private CountPolicy totalPagedResultsPolicy = CountPolicy.NONE;
425         private int pagedResultsOffset = 0;
426         private int pageSize = 0;
427         private String queryId;
428         private String queryExpression;
429 
430         private QueryRequestImpl() {
431             // Default constructor.
432         }
433 
434         private QueryRequestImpl(final QueryRequest request) {
435             super(request);
436             this.filter = request.getQueryFilter();
437             this.queryId = request.getQueryId();
438             this.queryExpression = request.getQueryExpression();
439             this.keys.addAll(request.getSortKeys());
440             this.pageSize = request.getPageSize();
441             this.pagedResultsCookie = request.getPagedResultsCookie();
442             this.pagedResultsOffset = request.getPagedResultsOffset();
443             this.totalPagedResultsPolicy = request.getTotalPagedResultsPolicy();
444         }
445 
446         @Override
447         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
448             return v.visitQueryRequest(p, this);
449         }
450 
451         @Override
452         public QueryRequest addSortKey(final SortKey... keys) {
453             for (final SortKey key : keys) {
454                 this.keys.add(notNull(key));
455             }
456             return this;
457         }
458 
459         @Override
460         public final QueryRequest addSortKey(final String... keys) {
461             for (final String key : keys) {
462                 this.keys.add(SortKey.valueOf(key));
463             }
464             return this;
465         }
466 
467         @Override
468         public String getPagedResultsCookie() {
469             return pagedResultsCookie;
470         }
471 
472         @Override
473         public CountPolicy getTotalPagedResultsPolicy() {
474             return totalPagedResultsPolicy;
475         }
476 
477         @Override
478         public int getPagedResultsOffset() {
479             return pagedResultsOffset;
480         }
481 
482         @Override
483         public int getPageSize() {
484             return pageSize;
485         }
486 
487         @Override
488         public org.forgerock.util.query.QueryFilter<JsonPointer> getQueryFilter() {
489             return filter;
490         }
491 
492         @Override
493         public String getQueryId() {
494             return queryId;
495         }
496 
497         @Override
498         public String getQueryExpression() {
499             return queryExpression;
500         }
501 
502         @Override
503         public List<SortKey> getSortKeys() {
504             return keys;
505         }
506 
507         @Override
508         public QueryRequest setPagedResultsCookie(final String cookie) {
509             this.pagedResultsCookie = cookie;
510             return this;
511         }
512 
513         @Override
514         public QueryRequest setTotalPagedResultsPolicy(final CountPolicy totalPagedResultsPolicy) {
515             this.totalPagedResultsPolicy = notNull(totalPagedResultsPolicy);
516             return this;
517         }
518 
519         @Override
520         public QueryRequest setPagedResultsOffset(int offset) {
521             this.pagedResultsOffset = offset;
522             return this;
523         }
524 
525         @Override
526         public QueryRequest setPageSize(final int size) {
527             this.pageSize = size;
528             return this;
529         }
530 
531         @Override
532         public QueryRequest setQueryExpression(final String expression) {
533             this.queryExpression = expression;
534             return this;
535         }
536 
537         @Override
538         public QueryRequest setQueryFilter(final org.forgerock.util.query.QueryFilter<JsonPointer> filter) {
539             this.filter = filter;
540             return this;
541         }
542 
543         @Override
544         public QueryRequest setQueryId(final String id) {
545             this.queryId = id;
546             return this;
547         }
548 
549         @Override
550         protected QueryRequest getThis() {
551             return this;
552         }
553 
554         @Override
555         public RequestType getRequestType() {
556             return RequestType.QUERY;
557         }
558 
559         @Override
560         public JsonValue toJsonValue() {
561             final List<String> sortKeys =  new ArrayList<>();
562             for (SortKey key : getSortKeys()) {
563                 sortKeys.add(String.valueOf(key));
564             }
565             return super.toJsonValue()
566                     .put(FIELD_QUERY_ID, String.valueOf(getQueryId()))
567                     .put(FIELD_QUERY_EXPRESSION, String.valueOf(getQueryExpression()))
568                     .put(FIELD_QUERY_FILTER, String.valueOf(getQueryFilter()))
569                     .put(FIELD_SORT_KEYS, sortKeys)
570                     .put(FIELD_PAGE_SIZE, String.valueOf(getPageSize()))
571                     .put(FIELD_PAGED_RESULTS_OFFSET, String.valueOf(getPagedResultsOffset()))
572                     .put(FIELD_PAGED_RESULTS_COOKIE, String.valueOf(getPagedResultsCookie()))
573                     .put(FIELD_TOTAL_PAGED_RESULTS_POLICY, String.valueOf(getTotalPagedResultsPolicy()))
574                     .put(FIELD_ADDITIONAL_PARAMETERS, getAdditionalParameters());
575         }
576     }
577 
578     private static final class ReadRequestImpl extends AbstractRequestImpl<ReadRequest> implements
579             ReadRequest {
580 
581         private ReadRequestImpl() {
582             // Default constructor.
583         }
584 
585         private ReadRequestImpl(final ReadRequest request) {
586             super(request);
587         }
588 
589         @Override
590         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
591             return v.visitReadRequest(p, this);
592         }
593 
594         @Override
595         protected ReadRequest getThis() {
596             return this;
597         }
598 
599         @Override
600         public RequestType getRequestType() {
601             return RequestType.READ;
602         }
603     }
604 
605     private static final class UpdateRequestImpl extends AbstractRequestImpl<UpdateRequest>
606             implements UpdateRequest {
607         private JsonValue content;
608         private String version;
609 
610         private UpdateRequestImpl() {
611             // Default constructor.
612         }
613 
614         private UpdateRequestImpl(final UpdateRequest request) {
615             super(request);
616             this.version = request.getRevision();
617             this.content = copyJsonValue(request.getContent());
618         }
619 
620         @Override
621         public <R, P> R accept(final RequestVisitor<R, P> v, final P p) {
622             return v.visitUpdateRequest(p, this);
623         }
624 
625         @Override
626         public JsonValue getContent() {
627             return content;
628         }
629 
630         @Override
631         public String getRevision() {
632             return version;
633         }
634 
635         @Override
636         public UpdateRequest setContent(final JsonValue content) {
637             this.content = notNull(content);
638             return this;
639         }
640 
641         @Override
642         public UpdateRequest setRevision(final String version) {
643             this.version = version;
644             return this;
645         }
646 
647         @Override
648         protected UpdateRequest getThis() {
649             return this;
650         }
651 
652         @Override
653         public RequestType getRequestType() {
654             return RequestType.UPDATE;
655         }
656 
657         @Override
658         public JsonValue toJsonValue() {
659             return super.toJsonValue()
660                     .put(FIELD_REVISION, String.valueOf(getRevision()))
661                     .put(FIELD_CONTENT, getContent().getObject());
662         }
663     }
664 
665     private static final class ApiRequestImpl extends AbstractRequestImpl<Request> {
666         private ApiRequestImpl() {
667            // nothing to do
668         }
669 
670         private ApiRequestImpl(ApiRequestImpl request) {
671             super(request);
672         }
673 
674         @Override
675         protected Request getThis() {
676             return this;
677         }
678 
679         @Override
680         public <R, P> R accept(RequestVisitor<R, P> v, P p) {
681             throw new UnsupportedOperationException();
682         }
683 
684         @Override
685         public RequestType getRequestType() {
686             return RequestType.API;
687         }
688     }
689 
690     /**
691      * Returns a copy of the provided action request.
692      *
693      * @param request
694      *            The action request to be copied.
695      * @return The action request copy.
696      */
697     public static ActionRequest copyOfActionRequest(final ActionRequest request) {
698         return new ActionRequestImpl(request);
699     }
700 
701     /**
702      * Returns a copy of the provided create request.
703      *
704      * @param request
705      *            The create request to be copied.
706      * @return The create request copy.
707      */
708     public static CreateRequest copyOfCreateRequest(final CreateRequest request) {
709         return new CreateRequestImpl(request);
710     }
711 
712     /**
713      * Returns a copy of the provided delete request.
714      *
715      * @param request
716      *            The delete request to be copied.
717      * @return The delete request copy.
718      */
719     public static DeleteRequest copyOfDeleteRequest(final DeleteRequest request) {
720         return new DeleteRequestImpl(request);
721     }
722 
723     /**
724      * Returns a copy of the provided patch request.
725      *
726      * @param request
727      *            The patch request to be copied.
728      * @return The patch request copy.
729      */
730     public static PatchRequest copyOfPatchRequest(final PatchRequest request) {
731         return new PatchRequestImpl(request);
732     }
733 
734     /**
735      * Returns a copy of the provided query request.
736      *
737      * @param request
738      *            The query request to be copied.
739      * @return The query request copy.
740      */
741     public static QueryRequest copyOfQueryRequest(final QueryRequest request) {
742         return new QueryRequestImpl(request);
743     }
744 
745     /**
746      * Returns a copy of the provided read request.
747      *
748      * @param request
749      *            The read request to be copied.
750      * @return The read request copy.
751      */
752     public static ReadRequest copyOfReadRequest(final ReadRequest request) {
753         return new ReadRequestImpl(request);
754     }
755 
756     /**
757      * Returns a copy of the provided update request.
758      *
759      * @param request
760      *            The update request to be copied.
761      * @return The update request copy.
762      */
763     public static UpdateRequest copyOfUpdateRequest(final UpdateRequest request) {
764         return new UpdateRequestImpl(request);
765     }
766 
767     /**
768      * Returns a copy of the provided api request.
769      *
770      * @param request
771      *            The api request to be copied.
772      * @return The api request copy.
773      */
774     public static Request copyOfApiRequest(final Request request) {
775         return new ApiRequestImpl((ApiRequestImpl) request);
776     }
777 
778     /**
779      * Returns a new action request with the provided resource path and action
780      * ID. Invoking this method as follows:
781      *
782      * <pre>
783      * newActionRequest(&quot;users/1&quot;, actionId);
784      * </pre>
785      *
786      * Is equivalent to:
787      *
788      * <pre>
789      * newActionRequest(&quot;users&quot;, &quot;1&quot;, actionId);
790      * </pre>
791      *
792      * Except that the resource ID is already URL encoded in the first form.
793      *
794      * @param resourcePath
795      *            The URL-encoded resource path.
796      * @param actionId
797      *            The action ID.
798      * @return The new action request.
799      */
800     public static ActionRequest newActionRequest(final String resourcePath, final String actionId) {
801         return new ActionRequestImpl().setResourcePath(resourcePath).setAction(actionId);
802     }
803 
804     /**
805      * Returns a new action request with the provided resource path and action
806      * ID.
807      *
808      * @param resourcePath
809      *            The parsed resource path.
810      * @param actionId
811      *            The action ID.
812      * @return The new action request.
813      */
814     public static ActionRequest newActionRequest(final ResourcePath resourcePath,
815             final String actionId) {
816         return new ActionRequestImpl().setResourcePath(resourcePath).setAction(actionId);
817     }
818 
819     /**
820      * Returns a new action request with the provided resource container path,
821      * resource ID, and action ID. Invoking this method as follows:
822      *
823      * <pre>
824      * newActionRequest(&quot;users&quot;, &quot;1&quot;, &quot;someAction&quot;);
825      * </pre>
826      *
827      * Is equivalent to:
828      *
829      * <pre>
830      * newActionRequest(&quot;users/1&quot;, &quot;someAction&quot;);
831      * </pre>
832      *
833      * Except that the resource ID is already URL encoded in the second form.
834      *
835      * @param resourceContainer
836      *            The URL-encoded path of the resource container.
837      * @param resourceId
838      *            The URL decoded ID of the resource.
839      * @param actionId
840      *            The action ID.
841      * @return The new action request.
842      */
843     public static ActionRequest newActionRequest(final String resourceContainer,
844             final String resourceId, final String actionId) {
845         return newActionRequest(ResourcePath.valueOf(resourceContainer), resourceId, actionId);
846     }
847 
848     /**
849      * Returns a new action request with the provided resource container path,
850      * resource ID, and action ID.
851      *
852      * @param resourceContainer
853      *            The parsed path of the resource container.
854      * @param resourceId
855      *            The URL decoded ID of the resource.
856      * @param actionId
857      *            The action ID.
858      * @return The new action request.
859      */
860     public static ActionRequest newActionRequest(final ResourcePath resourceContainer,
861             final String resourceId, final String actionId) {
862         return newActionRequest(resourceContainer.child(resourceId), actionId);
863     }
864 
865     /**
866      * Returns a new create request with the provided resource path, and JSON
867      * content. The create request will have a {@code null} new resource ID,
868      * indicating that the server will be responsible for generating the ID of
869      * the new resource. Invoking this method as follows:
870      *
871      * <pre>
872      * newCreateRequest(&quot;users/1&quot;, content);
873      * </pre>
874      *
875      * Is equivalent to:
876      *
877      * <pre>
878      * newCreateRequest(&quot;users&quot;, "1", content);
879      * </pre>
880      *
881      * Except that the resource ID is already URL encoded in the first form.
882      *
883      * @param resourceContainer
884      *            The URL-encoded path of the resource container beneath which the new
885      *            resource should be created.
886      * @param content
887      *            The JSON content.
888      * @return The new create request.
889      */
890     public static CreateRequest newCreateRequest(final String resourceContainer,
891             final JsonValue content) {
892         return new CreateRequestImpl().setResourcePath(resourceContainer).setContent(content);
893     }
894 
895     /**
896      * Returns a new create request with the provided resource path, and JSON
897      * content. The create request will have a {@code null} new resource ID,
898      * indicating that the server will be responsible for generating the ID of
899      * the new resource.
900      *
901      * @param resourceContainer
902      *            The parsed path of the resource container beneath which the
903      *            new resource should be created.
904      * @param content
905      *            The JSON content.
906      * @return The new create request.
907      */
908     public static CreateRequest newCreateRequest(final ResourcePath resourceContainer,
909             final JsonValue content) {
910         return new CreateRequestImpl().setResourcePath(resourceContainer).setContent(content);
911     }
912 
913     /**
914      * Returns a new create request with the provided resource path, new
915      * resource ID, and JSON content. Invoking this method as follows:
916      *
917      * <pre>
918      * newCreateRequest(&quot;users&quot;, &quot;1&quot;, content);
919      * </pre>
920      *
921      * Is equivalent to:
922      *
923      * <pre>
924      * newCreateRequest(&quot;users&quot;, content).setNewResourceId(&quot;1&quot;);
925      * </pre>
926      *
927      * Except that the resource ID is already URL encoded in the second form.
928      *
929      * @param resourceContainer
930      *            The URL-encoded path of the resource container beneath which
931      *            the new resource should be created.
932      * @param newResourceId
933      *            The URL decoded client provided ID of the resource to be
934      *            created, or {@code null} if the server should be responsible
935      *            for generating the resource ID.
936      * @param content
937      *            The JSON content.
938      * @return The new create request.
939      */
940     public static CreateRequest newCreateRequest(final String resourceContainer,
941             final String newResourceId, final JsonValue content) {
942         return newCreateRequest(resourceContainer, content).setNewResourceId(newResourceId);
943     }
944 
945     /**
946      * Returns a new create request with the provided resource path, new
947      * resource ID, and JSON content.
948      *
949      * @param resourceContainer
950      *            The parsed path of the resource container beneath which the
951      *            new resource should be created.
952      * @param newResourceId
953      *            The URL decoded client provided ID of the resource to be
954      *            created, or {@code null} if the server should be responsible
955      *            for generating the resource ID.
956      * @param content
957      *            The JSON content.
958      * @return The new create request.
959      */
960     public static CreateRequest newCreateRequest(final ResourcePath resourceContainer,
961             final String newResourceId, final JsonValue content) {
962         return newCreateRequest(resourceContainer, content).setNewResourceId(newResourceId);
963     }
964 
965     /**
966      * Returns a new delete request with the provided resource path. Invoking
967      * this method as follows:
968      *
969      * <pre>
970      * newDeleteRequest(&quot;users/1&quot;);
971      * </pre>
972      *
973      * Is equivalent to:
974      *
975      * <pre>
976      * newDeleteRequest(&quot;users&quot;, &quot;1&quot;);
977      * </pre>
978      *
979      * Except that the resource ID is already URL encoded in the first form.
980      *
981      * @param resourcePath
982      *            The URL-encoded resource path.
983      * @return The new delete request.
984      */
985     public static DeleteRequest newDeleteRequest(final String resourcePath) {
986         return new DeleteRequestImpl().setResourcePath(resourcePath);
987     }
988 
989     /**
990      * Returns a new delete request with the provided resource path.
991      *
992      * @param resourcePath
993      *            The parsed resource path.
994      * @return The new delete request.
995      */
996     public static DeleteRequest newDeleteRequest(final ResourcePath resourcePath) {
997         return new DeleteRequestImpl().setResourcePath(resourcePath);
998     }
999 
1000     /**
1001      * Returns a new delete request with the provided resource container path,
1002      * and resource ID. Invoking this method as follows:
1003      *
1004      * <pre>
1005      * newDeleteRequest(&quot;users&quot;, &quot;1&quot;);
1006      * </pre>
1007      *
1008      * Is equivalent to:
1009      *
1010      * <pre>
1011      * newDeleteRequest(&quot;users/1&quot;);
1012      * </pre>
1013      *
1014      * Except that the resource ID is already URL encoded in the second form.
1015      *
1016      * @param resourceContainer
1017      *            The URL-encoded path of the resource container.
1018      * @param resourceId
1019      *            The URL decoded ID of the resource.
1020      * @return The new delete request.
1021      */
1022     public static DeleteRequest newDeleteRequest(final String resourceContainer,
1023             final String resourceId) {
1024         return newDeleteRequest(ResourcePath.valueOf(resourceContainer), resourceId);
1025     }
1026 
1027     /**
1028      * Returns a new delete request with the provided resource container path,
1029      * and resource ID.
1030      *
1031      * @param resourceContainer
1032      *            The parsed path of the resource container.
1033      * @param resourceId
1034      *            The URL decoded ID of the resource.
1035      * @return The new delete request.
1036      */
1037     public static DeleteRequest newDeleteRequest(final ResourcePath resourceContainer,
1038             final String resourceId) {
1039         return newDeleteRequest(resourceContainer.child(resourceId));
1040     }
1041 
1042     /**
1043      * Returns a new patch request with the provided resource path and JSON
1044      * patch operations. Invoking this method as follows:
1045      *
1046      * <pre>
1047      * newPatchRequest(&quot;users/1&quot;, operations);
1048      * </pre>
1049      *
1050      * Is equivalent to:
1051      *
1052      * <pre>
1053      * newPatchRequest(&quot;users&quot;, &quot;1&quot;, operations);
1054      * </pre>
1055      *
1056      * Except that the resource ID is already URL encoded in the first form.
1057      *
1058      * @param resourcePath
1059      *            The URL-encoded resource path.
1060      * @param operations
1061      *            The JSON patch operations.
1062      * @return The new patch request.
1063      */
1064     public static PatchRequest newPatchRequest(final String resourcePath,
1065             final PatchOperation... operations) {
1066         return new PatchRequestImpl().setResourcePath(resourcePath).addPatchOperation(operations);
1067     }
1068 
1069     /**
1070      * Returns a new patch request with the provided resource path and JSON
1071      * patch operations.
1072      *
1073      * @param resourcePath
1074      *            The parsed resource path.
1075      * @param operations
1076      *            The JSON patch operations.
1077      * @return The new patch request.
1078      */
1079     public static PatchRequest newPatchRequest(final ResourcePath resourcePath,
1080             final PatchOperation... operations) {
1081         return new PatchRequestImpl().setResourcePath(resourcePath).addPatchOperation(operations);
1082     }
1083 
1084     /**
1085      * Returns a new patch request with the provided resource container path,
1086      * resource ID, and JSON patch operations. Invoking this method as follows:
1087      *
1088      * <pre>
1089      * newPatchRequest(&quot;users&quot;, &quot;1&quot;, operations);
1090      * </pre>
1091      *
1092      * Is equivalent to:
1093      *
1094      * <pre>
1095      * newPatchRequest(&quot;users/1&quot;, operations);
1096      * </pre>
1097      *
1098      * Except that the resource ID is already URL encoded in the second form.
1099      *
1100      * @param resourceContainer
1101      *            The URL-encoded path of the resource container.
1102      * @param resourceId
1103      *            The URL decoded ID of the resource.
1104      * @param operations
1105      *            The JSON patch operations.
1106      * @return The new patch request.
1107      */
1108     public static PatchRequest newPatchRequest(final String resourceContainer,
1109             final String resourceId, final PatchOperation... operations) {
1110         return newPatchRequest(ResourcePath.valueOf(resourceContainer), resourceId, operations);
1111     }
1112 
1113     /**
1114      * Returns a new patch request with the provided resource container path,
1115      * resource ID, and JSON patch operations.
1116      *
1117      * @param resourceContainer
1118      *            The parsed path of the resource container.
1119      * @param resourceId
1120      *            The URL decoded ID of the resource.
1121      * @param operations
1122      *            The JSON patch operations.
1123      * @return The new patch request.
1124      */
1125     public static PatchRequest newPatchRequest(final ResourcePath resourceContainer,
1126             final String resourceId, final PatchOperation... operations) {
1127         return newPatchRequest(resourceContainer.child(resourceId), operations);
1128     }
1129 
1130     /**
1131      * Returns a new query request with the provided resource container path.
1132      * Example:
1133      *
1134      * <pre>
1135      * newQueryRequest(&quot;users&quot;);
1136      * </pre>
1137      *
1138      * @param resourceContainer
1139      *            The URL-encoded path of the resource container.
1140      * @return The new query request.
1141      */
1142     public static QueryRequest newQueryRequest(final String resourceContainer) {
1143         return new QueryRequestImpl().setResourcePath(resourceContainer);
1144     }
1145 
1146     /**
1147      * Returns a new query request with the provided resource container path.
1148      * Example:
1149      *
1150      * <pre>
1151      * newQueryRequest(ResourcePath.valueOf(&quot;users&quot;));
1152      * </pre>
1153      *
1154      * @param resourceContainer
1155      *            The parsed path of the resource container.
1156      * @return The new query request.
1157      */
1158     public static QueryRequest newQueryRequest(final ResourcePath resourceContainer) {
1159         return new QueryRequestImpl().setResourcePath(resourceContainer);
1160     }
1161 
1162     /**
1163      * Returns a new read request with the provided resource path. Invoking this
1164      * method as follows:
1165      *
1166      * <pre>
1167      * newReadRequest(&quot;users/1&quot;);
1168      * </pre>
1169      *
1170      * Is equivalent to:
1171      *
1172      * <pre>
1173      * newReadRequest(&quot;users&quot;, &quot;1&quot;);
1174      * </pre>
1175      *
1176      * Except that the resource ID is already URL encoded in the first form.
1177      *
1178      * @param resourcePath
1179      *            The URL-encoded resource path.
1180      * @return The new read request.
1181      */
1182     public static ReadRequest newReadRequest(final String resourcePath) {
1183         return new ReadRequestImpl().setResourcePath(resourcePath);
1184     }
1185 
1186     /**
1187      * Returns a new read request with the provided resource path.
1188      *
1189      * @param resourcePath
1190      *            The parsed resource path.
1191      * @return The new read request.
1192      */
1193     public static ReadRequest newReadRequest(final ResourcePath resourcePath) {
1194         return new ReadRequestImpl().setResourcePath(resourcePath);
1195     }
1196 
1197     /**
1198      * Returns a new read request with the provided resource container path, and
1199      * resource ID. Invoking this method as follows:
1200      *
1201      * <pre>
1202      * newReadRequest(&quot;users&quot;, &quot;1&quot;);
1203      * </pre>
1204      *
1205      * Is equivalent to:
1206      *
1207      * <pre>
1208      * newReadRequest(&quot;users/1&quot;);
1209      * </pre>
1210      *
1211      * Except that the resource ID is already URL encoded in the second form.
1212      *
1213      * @param resourceContainer
1214      *            The URL-encoded path of the resource container.
1215      * @param resourceId
1216      *            The URL decoded ID of the resource.
1217      * @return The new read request.
1218      */
1219     public static ReadRequest newReadRequest(final String resourceContainer, final String resourceId) {
1220         return newReadRequest(ResourcePath.valueOf(resourceContainer), resourceId);
1221     }
1222 
1223     /**
1224      * Returns a new read request with the provided resource container path, and
1225      * resource ID.
1226      *
1227      * @param resourceContainer
1228      *            The parsed path of the resource container.
1229      * @param resourceId
1230      *            The URL decoded ID of the resource.
1231      * @return The new read request.
1232      */
1233     public static ReadRequest newReadRequest(final ResourcePath resourceContainer,
1234             final String resourceId) {
1235         return newReadRequest(resourceContainer.child(resourceId));
1236     }
1237 
1238     /**
1239      * Returns a new update request with the provided resource path and new JSON
1240      * content. Invoking this method as follows:
1241      *
1242      * <pre>
1243      * newUpdateRequest(&quot;users/1&quot;, newContent);
1244      * </pre>
1245      *
1246      * Is equivalent to:
1247      *
1248      * <pre>
1249      * newUpdateRequest(&quot;users&quot;, &quot;1&quot;, newContent);
1250      * </pre>
1251      *
1252      * Except that the resource ID is already URL encoded in the first form.
1253      *
1254      * @param resourcePath
1255      *            The URL-encoded resource path.
1256      * @param newContent
1257      *            The new JSON content.
1258      * @return The new update request.
1259      */
1260     public static UpdateRequest newUpdateRequest(final String resourcePath,
1261             final JsonValue newContent) {
1262         return new UpdateRequestImpl().setResourcePath(resourcePath).setContent(newContent);
1263     }
1264 
1265     /**
1266      * Returns a new update request with the provided resource path and new JSON
1267      * content.
1268      *
1269      * @param resourcePath
1270      *            The parsed resource path.
1271      * @param newContent
1272      *            The new JSON content.
1273      * @return The new update request.
1274      */
1275     public static UpdateRequest newUpdateRequest(final ResourcePath resourcePath,
1276             final JsonValue newContent) {
1277         return new UpdateRequestImpl().setResourcePath(resourcePath).setContent(newContent);
1278     }
1279 
1280     /**
1281      * Returns a new update request with the provided resource container path,
1282      * resource ID, and new JSON content. Invoking this method as follows:
1283      *
1284      * <pre>
1285      * newUpdateRequest(&quot;users&quot;, &quot;1&quot;, newContent);
1286      * </pre>
1287      *
1288      * Is equivalent to:
1289      *
1290      * <pre>
1291      * newUpdateRequest(&quot;users/1&quot;, newContent);
1292      * </pre>
1293      *
1294      * Except that the resource ID is already URL encoded in the second form.
1295      *
1296      * @param resourceContainer
1297      *            The URL-encoded path of the resource container.
1298      * @param resourceId
1299      *            The URL decoded ID of the resource.
1300      * @param newContent
1301      *            The new JSON content.
1302      * @return The new update request.
1303      */
1304     public static UpdateRequest newUpdateRequest(final String resourceContainer,
1305             final String resourceId, final JsonValue newContent) {
1306         return newUpdateRequest(ResourcePath.valueOf(resourceContainer), resourceId, newContent);
1307     }
1308 
1309     /**
1310      * Returns a new update request with the provided resource container path,
1311      * resource ID, and new JSON content.
1312      *
1313      * @param resourceContainer
1314      *            The parsed path of the resource container.
1315      * @param resourceId
1316      *            The URL decoded ID of the resource.
1317      * @param newContent
1318      *            The new JSON content.
1319      * @return The new update request.
1320      */
1321     public static UpdateRequest newUpdateRequest(final ResourcePath resourceContainer,
1322             final String resourceId, final JsonValue newContent) {
1323         return newUpdateRequest(resourceContainer.child(resourceId), newContent);
1324     }
1325 
1326     /**
1327      * Returns a new API request with the provided path.
1328      * @param path The path.
1329      * @return The request.
1330      */
1331     public static Request newApiRequest(final ResourcePath path) {
1332         return new ApiRequestImpl().setResourcePath(path);
1333     }
1334 
1335     private static JsonValue copyJsonValue(final JsonValue value) {
1336         return value != null ? value.copy() : null;
1337     }
1338 
1339     private static <T> T notNull(final T object) {
1340         if (object != null) {
1341             return object;
1342         } else {
1343             throw new NullPointerException();
1344         }
1345     }
1346 
1347     private Requests() {
1348         // Prevent instantiation.
1349     }
1350 
1351     // TODO: unmodifiable
1352 }