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-2015 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    /**
666     * Returns a copy of the provided action request.
667     *
668     * @param request
669     *            The action request to be copied.
670     * @return The action request copy.
671     */
672    public static ActionRequest copyOfActionRequest(final ActionRequest request) {
673        return new ActionRequestImpl(request);
674    }
675
676    /**
677     * Returns a copy of the provided create request.
678     *
679     * @param request
680     *            The create request to be copied.
681     * @return The create request copy.
682     */
683    public static CreateRequest copyOfCreateRequest(final CreateRequest request) {
684        return new CreateRequestImpl(request);
685    }
686
687    /**
688     * Returns a copy of the provided delete request.
689     *
690     * @param request
691     *            The delete request to be copied.
692     * @return The delete request copy.
693     */
694    public static DeleteRequest copyOfDeleteRequest(final DeleteRequest request) {
695        return new DeleteRequestImpl(request);
696    }
697
698    /**
699     * Returns a copy of the provided patch request.
700     *
701     * @param request
702     *            The patch request to be copied.
703     * @return The patch request copy.
704     */
705    public static PatchRequest copyOfPatchRequest(final PatchRequest request) {
706        return new PatchRequestImpl(request);
707    }
708
709    /**
710     * Returns a copy of the provided query request.
711     *
712     * @param request
713     *            The query request to be copied.
714     * @return The query request copy.
715     */
716    public static QueryRequest copyOfQueryRequest(final QueryRequest request) {
717        return new QueryRequestImpl(request);
718    }
719
720    /**
721     * Returns a copy of the provided read request.
722     *
723     * @param request
724     *            The read request to be copied.
725     * @return The read request copy.
726     */
727    public static ReadRequest copyOfReadRequest(final ReadRequest request) {
728        return new ReadRequestImpl(request);
729    }
730
731    /**
732     * Returns a copy of the provided update request.
733     *
734     * @param request
735     *            The update request to be copied.
736     * @return The update request copy.
737     */
738    public static UpdateRequest copyOfUpdateRequest(final UpdateRequest request) {
739        return new UpdateRequestImpl(request);
740    }
741
742    /**
743     * Returns a new action request with the provided resource path and action
744     * ID. Invoking this method as follows:
745     *
746     * <pre>
747     * newActionRequest(&quot;users/1&quot;, actionId);
748     * </pre>
749     *
750     * Is equivalent to:
751     *
752     * <pre>
753     * newActionRequest(&quot;users&quot;, &quot;1&quot;, actionId);
754     * </pre>
755     *
756     * Except that the resource ID is already URL encoded in the first form.
757     *
758     * @param resourcePath
759     *            The URL-encoded resource path.
760     * @param actionId
761     *            The action ID.
762     * @return The new action request.
763     */
764    public static ActionRequest newActionRequest(final String resourcePath, final String actionId) {
765        return new ActionRequestImpl().setResourcePath(resourcePath).setAction(actionId);
766    }
767
768    /**
769     * Returns a new action request with the provided resource path and action
770     * ID.
771     *
772     * @param resourcePath
773     *            The parsed resource path.
774     * @param actionId
775     *            The action ID.
776     * @return The new action request.
777     */
778    public static ActionRequest newActionRequest(final ResourcePath resourcePath,
779            final String actionId) {
780        return new ActionRequestImpl().setResourcePath(resourcePath).setAction(actionId);
781    }
782
783    /**
784     * Returns a new action request with the provided resource container path,
785     * resource ID, and action ID. Invoking this method as follows:
786     *
787     * <pre>
788     * newActionRequest(&quot;users&quot;, &quot;1&quot;, &quot;someAction&quot;);
789     * </pre>
790     *
791     * Is equivalent to:
792     *
793     * <pre>
794     * newActionRequest(&quot;users/1&quot;, &quot;someAction&quot;);
795     * </pre>
796     *
797     * Except that the resource ID is already URL encoded in the second form.
798     *
799     * @param resourceContainer
800     *            The URL-encoded path of the resource container.
801     * @param resourceId
802     *            The URL decoded ID of the resource.
803     * @param actionId
804     *            The action ID.
805     * @return The new action request.
806     */
807    public static ActionRequest newActionRequest(final String resourceContainer,
808            final String resourceId, final String actionId) {
809        return newActionRequest(ResourcePath.valueOf(resourceContainer), resourceId, actionId);
810    }
811
812    /**
813     * Returns a new action request with the provided resource container path,
814     * resource ID, and action ID.
815     *
816     * @param resourceContainer
817     *            The parsed path of the resource container.
818     * @param resourceId
819     *            The URL decoded ID of the resource.
820     * @param actionId
821     *            The action ID.
822     * @return The new action request.
823     */
824    public static ActionRequest newActionRequest(final ResourcePath resourceContainer,
825            final String resourceId, final String actionId) {
826        return newActionRequest(resourceContainer.child(resourceId), actionId);
827    }
828
829    /**
830     * Returns a new create request with the provided resource path, and JSON
831     * content. The create request will have a {@code null} new resource ID,
832     * indicating that the server will be responsible for generating the ID of
833     * the new resource. Invoking this method as follows:
834     *
835     * <pre>
836     * newCreateRequest(&quot;users/1&quot;, content);
837     * </pre>
838     *
839     * Is equivalent to:
840     *
841     * <pre>
842     * newCreateRequest(&quot;users&quot;, "1", content);
843     * </pre>
844     *
845     * Except that the resource ID is already URL encoded in the first form.
846     *
847     * @param resourceContainer
848     *            The URL-encoded path of the resource container beneath which the new
849     *            resource should be created.
850     * @param content
851     *            The JSON content.
852     * @return The new create request.
853     */
854    public static CreateRequest newCreateRequest(final String resourceContainer,
855            final JsonValue content) {
856        return new CreateRequestImpl().setResourcePath(resourceContainer).setContent(content);
857    }
858
859    /**
860     * Returns a new create request with the provided resource path, and JSON
861     * content. The create request will have a {@code null} new resource ID,
862     * indicating that the server will be responsible for generating the ID of
863     * the new resource.
864     *
865     * @param resourceContainer
866     *            The parsed path of the resource container beneath which the
867     *            new resource should be created.
868     * @param content
869     *            The JSON content.
870     * @return The new create request.
871     */
872    public static CreateRequest newCreateRequest(final ResourcePath resourceContainer,
873            final JsonValue content) {
874        return new CreateRequestImpl().setResourcePath(resourceContainer).setContent(content);
875    }
876
877    /**
878     * Returns a new create request with the provided resource path, new
879     * resource ID, and JSON content. Invoking this method as follows:
880     *
881     * <pre>
882     * newCreateRequest(&quot;users&quot;, &quot;1&quot;, content);
883     * </pre>
884     *
885     * Is equivalent to:
886     *
887     * <pre>
888     * newCreateRequest(&quot;users&quot;, content).setNewResourceId(&quot;1&quot;);
889     * </pre>
890     *
891     * Except that the resource ID is already URL encoded in the second form.
892     *
893     * @param resourceContainer
894     *            The URL-encoded path of the resource container beneath which
895     *            the new resource should be created.
896     * @param newResourceId
897     *            The URL decoded client provided ID of the resource to be
898     *            created, or {@code null} if the server should be responsible
899     *            for generating the resource ID.
900     * @param content
901     *            The JSON content.
902     * @return The new create request.
903     */
904    public static CreateRequest newCreateRequest(final String resourceContainer,
905            final String newResourceId, final JsonValue content) {
906        return newCreateRequest(resourceContainer, content).setNewResourceId(newResourceId);
907    }
908
909    /**
910     * Returns a new create request with the provided resource path, new
911     * resource ID, and JSON content.
912     *
913     * @param resourceContainer
914     *            The parsed path of the resource container beneath which the
915     *            new resource should be created.
916     * @param newResourceId
917     *            The URL decoded client provided ID of the resource to be
918     *            created, or {@code null} if the server should be responsible
919     *            for generating the resource ID.
920     * @param content
921     *            The JSON content.
922     * @return The new create request.
923     */
924    public static CreateRequest newCreateRequest(final ResourcePath resourceContainer,
925            final String newResourceId, final JsonValue content) {
926        return newCreateRequest(resourceContainer, content).setNewResourceId(newResourceId);
927    }
928
929    /**
930     * Returns a new delete request with the provided resource path. Invoking
931     * this method as follows:
932     *
933     * <pre>
934     * newDeleteRequest(&quot;users/1&quot;);
935     * </pre>
936     *
937     * Is equivalent to:
938     *
939     * <pre>
940     * newDeleteRequest(&quot;users&quot;, &quot;1&quot;);
941     * </pre>
942     *
943     * Except that the resource ID is already URL encoded in the first form.
944     *
945     * @param resourcePath
946     *            The URL-encoded resource path.
947     * @return The new delete request.
948     */
949    public static DeleteRequest newDeleteRequest(final String resourcePath) {
950        return new DeleteRequestImpl().setResourcePath(resourcePath);
951    }
952
953    /**
954     * Returns a new delete request with the provided resource path.
955     *
956     * @param resourcePath
957     *            The parsed resource path.
958     * @return The new delete request.
959     */
960    public static DeleteRequest newDeleteRequest(final ResourcePath resourcePath) {
961        return new DeleteRequestImpl().setResourcePath(resourcePath);
962    }
963
964    /**
965     * Returns a new delete request with the provided resource container path,
966     * and resource ID. Invoking this method as follows:
967     *
968     * <pre>
969     * newDeleteRequest(&quot;users&quot;, &quot;1&quot;);
970     * </pre>
971     *
972     * Is equivalent to:
973     *
974     * <pre>
975     * newDeleteRequest(&quot;users/1&quot;);
976     * </pre>
977     *
978     * Except that the resource ID is already URL encoded in the second form.
979     *
980     * @param resourceContainer
981     *            The URL-encoded path of the resource container.
982     * @param resourceId
983     *            The URL decoded ID of the resource.
984     * @return The new delete request.
985     */
986    public static DeleteRequest newDeleteRequest(final String resourceContainer,
987            final String resourceId) {
988        return newDeleteRequest(ResourcePath.valueOf(resourceContainer), resourceId);
989    }
990
991    /**
992     * Returns a new delete request with the provided resource container path,
993     * and resource ID.
994     *
995     * @param resourceContainer
996     *            The parsed path of the resource container.
997     * @param resourceId
998     *            The URL decoded ID of the resource.
999     * @return The new delete request.
1000     */
1001    public static DeleteRequest newDeleteRequest(final ResourcePath resourceContainer,
1002            final String resourceId) {
1003        return newDeleteRequest(resourceContainer.child(resourceId));
1004    }
1005
1006    /**
1007     * Returns a new patch request with the provided resource path and JSON
1008     * patch operations. Invoking this method as follows:
1009     *
1010     * <pre>
1011     * newPatchRequest(&quot;users/1&quot;, operations);
1012     * </pre>
1013     *
1014     * Is equivalent to:
1015     *
1016     * <pre>
1017     * newPatchRequest(&quot;users&quot;, &quot;1&quot;, operations);
1018     * </pre>
1019     *
1020     * Except that the resource ID is already URL encoded in the first form.
1021     *
1022     * @param resourcePath
1023     *            The URL-encoded resource path.
1024     * @param operations
1025     *            The JSON patch operations.
1026     * @return The new patch request.
1027     */
1028    public static PatchRequest newPatchRequest(final String resourcePath,
1029            final PatchOperation... operations) {
1030        return new PatchRequestImpl().setResourcePath(resourcePath).addPatchOperation(operations);
1031    }
1032
1033    /**
1034     * Returns a new patch request with the provided resource path and JSON
1035     * patch operations.
1036     *
1037     * @param resourcePath
1038     *            The parsed resource path.
1039     * @param operations
1040     *            The JSON patch operations.
1041     * @return The new patch request.
1042     */
1043    public static PatchRequest newPatchRequest(final ResourcePath resourcePath,
1044            final PatchOperation... operations) {
1045        return new PatchRequestImpl().setResourcePath(resourcePath).addPatchOperation(operations);
1046    }
1047
1048    /**
1049     * Returns a new patch request with the provided resource container path,
1050     * resource ID, and JSON patch operations. Invoking this method as follows:
1051     *
1052     * <pre>
1053     * newPatchRequest(&quot;users&quot;, &quot;1&quot;, operations);
1054     * </pre>
1055     *
1056     * Is equivalent to:
1057     *
1058     * <pre>
1059     * newPatchRequest(&quot;users/1&quot;, operations);
1060     * </pre>
1061     *
1062     * Except that the resource ID is already URL encoded in the second form.
1063     *
1064     * @param resourceContainer
1065     *            The URL-encoded path of the resource container.
1066     * @param resourceId
1067     *            The URL decoded ID of the resource.
1068     * @param operations
1069     *            The JSON patch operations.
1070     * @return The new patch request.
1071     */
1072    public static PatchRequest newPatchRequest(final String resourceContainer,
1073            final String resourceId, final PatchOperation... operations) {
1074        return newPatchRequest(ResourcePath.valueOf(resourceContainer), resourceId, operations);
1075    }
1076
1077    /**
1078     * Returns a new patch request with the provided resource container path,
1079     * resource ID, and JSON patch operations.
1080     *
1081     * @param resourceContainer
1082     *            The parsed path of the resource container.
1083     * @param resourceId
1084     *            The URL decoded ID of the resource.
1085     * @param operations
1086     *            The JSON patch operations.
1087     * @return The new patch request.
1088     */
1089    public static PatchRequest newPatchRequest(final ResourcePath resourceContainer,
1090            final String resourceId, final PatchOperation... operations) {
1091        return newPatchRequest(resourceContainer.child(resourceId), operations);
1092    }
1093
1094    /**
1095     * Returns a new query request with the provided resource container path.
1096     * Example:
1097     *
1098     * <pre>
1099     * newQueryRequest(&quot;users&quot;);
1100     * </pre>
1101     *
1102     * @param resourceContainer
1103     *            The URL-encoded path of the resource container.
1104     * @return The new query request.
1105     */
1106    public static QueryRequest newQueryRequest(final String resourceContainer) {
1107        return new QueryRequestImpl().setResourcePath(resourceContainer);
1108    }
1109
1110    /**
1111     * Returns a new query request with the provided resource container path.
1112     * Example:
1113     *
1114     * <pre>
1115     * newQueryRequest(ResourcePath.valueOf(&quot;users&quot;));
1116     * </pre>
1117     *
1118     * @param resourceContainer
1119     *            The parsed path of the resource container.
1120     * @return The new query request.
1121     */
1122    public static QueryRequest newQueryRequest(final ResourcePath resourceContainer) {
1123        return new QueryRequestImpl().setResourcePath(resourceContainer);
1124    }
1125
1126    /**
1127     * Returns a new read request with the provided resource path. Invoking this
1128     * method as follows:
1129     *
1130     * <pre>
1131     * newReadRequest(&quot;users/1&quot;);
1132     * </pre>
1133     *
1134     * Is equivalent to:
1135     *
1136     * <pre>
1137     * newReadRequest(&quot;users&quot;, &quot;1&quot;);
1138     * </pre>
1139     *
1140     * Except that the resource ID is already URL encoded in the first form.
1141     *
1142     * @param resourcePath
1143     *            The URL-encoded resource path.
1144     * @return The new read request.
1145     */
1146    public static ReadRequest newReadRequest(final String resourcePath) {
1147        return new ReadRequestImpl().setResourcePath(resourcePath);
1148    }
1149
1150    /**
1151     * Returns a new read request with the provided resource path.
1152     *
1153     * @param resourcePath
1154     *            The parsed resource path.
1155     * @return The new read request.
1156     */
1157    public static ReadRequest newReadRequest(final ResourcePath resourcePath) {
1158        return new ReadRequestImpl().setResourcePath(resourcePath);
1159    }
1160
1161    /**
1162     * Returns a new read request with the provided resource container path, and
1163     * resource ID. Invoking this method as follows:
1164     *
1165     * <pre>
1166     * newReadRequest(&quot;users&quot;, &quot;1&quot;);
1167     * </pre>
1168     *
1169     * Is equivalent to:
1170     *
1171     * <pre>
1172     * newReadRequest(&quot;users/1&quot;);
1173     * </pre>
1174     *
1175     * Except that the resource ID is already URL encoded in the second form.
1176     *
1177     * @param resourceContainer
1178     *            The URL-encoded path of the resource container.
1179     * @param resourceId
1180     *            The URL decoded ID of the resource.
1181     * @return The new read request.
1182     */
1183    public static ReadRequest newReadRequest(final String resourceContainer, final String resourceId) {
1184        return newReadRequest(ResourcePath.valueOf(resourceContainer), resourceId);
1185    }
1186
1187    /**
1188     * Returns a new read request with the provided resource container path, and
1189     * resource ID.
1190     *
1191     * @param resourceContainer
1192     *            The parsed path of the resource container.
1193     * @param resourceId
1194     *            The URL decoded ID of the resource.
1195     * @return The new read request.
1196     */
1197    public static ReadRequest newReadRequest(final ResourcePath resourceContainer,
1198            final String resourceId) {
1199        return newReadRequest(resourceContainer.child(resourceId));
1200    }
1201
1202    /**
1203     * Returns a new update request with the provided resource path and new JSON
1204     * content. Invoking this method as follows:
1205     *
1206     * <pre>
1207     * newUpdateRequest(&quot;users/1&quot;, newContent);
1208     * </pre>
1209     *
1210     * Is equivalent to:
1211     *
1212     * <pre>
1213     * newUpdateRequest(&quot;users&quot;, &quot;1&quot;, newContent);
1214     * </pre>
1215     *
1216     * Except that the resource ID is already URL encoded in the first form.
1217     *
1218     * @param resourcePath
1219     *            The URL-encoded resource path.
1220     * @param newContent
1221     *            The new JSON content.
1222     * @return The new update request.
1223     */
1224    public static UpdateRequest newUpdateRequest(final String resourcePath,
1225            final JsonValue newContent) {
1226        return new UpdateRequestImpl().setResourcePath(resourcePath).setContent(newContent);
1227    }
1228
1229    /**
1230     * Returns a new update request with the provided resource path and new JSON
1231     * content.
1232     *
1233     * @param resourcePath
1234     *            The parsed resource path.
1235     * @param newContent
1236     *            The new JSON content.
1237     * @return The new update request.
1238     */
1239    public static UpdateRequest newUpdateRequest(final ResourcePath resourcePath,
1240            final JsonValue newContent) {
1241        return new UpdateRequestImpl().setResourcePath(resourcePath).setContent(newContent);
1242    }
1243
1244    /**
1245     * Returns a new update request with the provided resource container path,
1246     * resource ID, and new JSON content. Invoking this method as follows:
1247     *
1248     * <pre>
1249     * newUpdateRequest(&quot;users&quot;, &quot;1&quot;, newContent);
1250     * </pre>
1251     *
1252     * Is equivalent to:
1253     *
1254     * <pre>
1255     * newUpdateRequest(&quot;users/1&quot;, newContent);
1256     * </pre>
1257     *
1258     * Except that the resource ID is already URL encoded in the second form.
1259     *
1260     * @param resourceContainer
1261     *            The URL-encoded path of the resource container.
1262     * @param resourceId
1263     *            The URL decoded ID of the resource.
1264     * @param newContent
1265     *            The new JSON content.
1266     * @return The new update request.
1267     */
1268    public static UpdateRequest newUpdateRequest(final String resourceContainer,
1269            final String resourceId, final JsonValue newContent) {
1270        return newUpdateRequest(ResourcePath.valueOf(resourceContainer), resourceId, newContent);
1271    }
1272
1273    /**
1274     * Returns a new update request with the provided resource container path,
1275     * resource ID, and new JSON content.
1276     *
1277     * @param resourceContainer
1278     *            The parsed path of the resource container.
1279     * @param resourceId
1280     *            The URL decoded ID of the resource.
1281     * @param newContent
1282     *            The new JSON content.
1283     * @return The new update request.
1284     */
1285    public static UpdateRequest newUpdateRequest(final ResourcePath resourceContainer,
1286            final String resourceId, final JsonValue newContent) {
1287        return newUpdateRequest(resourceContainer.child(resourceId), newContent);
1288    }
1289
1290    private static JsonValue copyJsonValue(final JsonValue value) {
1291        return value != null ? value.copy() : null;
1292    }
1293
1294    private static <T> T notNull(final T object) {
1295        if (object != null) {
1296            return object;
1297        } else {
1298            throw new NullPointerException();
1299        }
1300    }
1301
1302    private Requests() {
1303        // Prevent instantiation.
1304    }
1305
1306    // TODO: unmodifiable
1307}