001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2012-2016 ForgeRock AS.
015 */
016
017package org.forgerock.json.resource;
018
019import static org.forgerock.util.Utils.*;
020
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.LinkedHashMap;
025import java.util.LinkedList;
026import java.util.List;
027import java.util.Map;
028
029import org.forgerock.http.routing.Version;
030import org.forgerock.json.JsonException;
031import org.forgerock.json.JsonPointer;
032import org.forgerock.json.JsonValue;
033import org.forgerock.util.i18n.PreferredLocales;
034
035/**
036 * A utility class containing various factory methods for creating and
037 * manipulating requests.
038 */
039public final class Requests {
040    private static abstract class AbstractRequestImpl<T extends Request> implements Request {
041        private final List<JsonPointer> fields = new LinkedList<>();
042        private ResourcePath resourcePath;
043        private final Map<String, String> parameters = new LinkedHashMap<>(2);
044        private Version resourceVersion;
045        private PreferredLocales preferredLocales;
046
047        protected AbstractRequestImpl() {
048            // Default constructor.
049        }
050
051        protected AbstractRequestImpl(final Request request) {
052            this.resourcePath = request.getResourcePathObject();
053            this.fields.addAll(request.getFields());
054            this.parameters.putAll(request.getAdditionalParameters());
055            this.resourceVersion = request.getResourceVersion();
056            this.preferredLocales = request.getPreferredLocales();
057        }
058
059        @Override
060        public final T addField(final JsonPointer... fields) {
061            for (final JsonPointer field : fields) {
062                this.fields.add(notNull(field));
063            }
064            return getThis();
065        }
066
067        @Override
068        public final T addField(final String... fields) {
069            try {
070                for (final String field : fields) {
071                    this.fields.add(new JsonPointer(field));
072                }
073            } catch (final JsonException e) {
074                throw new IllegalArgumentException(e.getMessage());
075            }
076            return getThis();
077        }
078
079        @Override
080        public final List<JsonPointer> getFields() {
081            return fields;
082        }
083
084        @Override
085        public final String getResourcePath() {
086            return resourcePath.toString();
087        }
088
089        @Override
090        public final ResourcePath getResourcePathObject() {
091            return resourcePath;
092        }
093
094        @Override
095        public Map<String, String> getAdditionalParameters() {
096            return parameters;
097        }
098
099        @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}