001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.httppart;
018
019import static org.apache.juneau.httppart.HttpPartType.*;
020
021import java.lang.reflect.*;
022import java.time.*;
023import java.util.*;
024import java.util.regex.*;
025
026import org.apache.http.*;
027import org.apache.juneau.*;
028import org.apache.juneau.assertions.*;
029import org.apache.juneau.common.utils.*;
030import org.apache.juneau.http.*;
031import org.apache.juneau.http.part.*;
032import org.apache.juneau.http.response.*;
033import org.apache.juneau.httppart.*;
034import org.apache.juneau.internal.*;
035import org.apache.juneau.oapi.*;
036import org.apache.juneau.parser.ParseException;
037import org.apache.juneau.reflect.*;
038import org.apache.juneau.rest.*;
039
040/**
041 * Represents a single HTTP part on an HTTP request.
042 *
043 * Parent of the following classes:
044 * <ul class='javatreec'>
045 *    <li class='jc'>{@link RequestHeader}
046 *    <li class='jc'>{@link RequestQueryParam}
047 *    <li class='jc'>{@link RequestFormParam}
048 *    <li class='jc'>{@link RequestPathParam}
049 * </ul>
050 *
051 * <h5 class='section'>See Also:</h5><ul>
052 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HttpParts">HTTP Parts</a>
053 * </ul>
054 */
055public class RequestHttpPart {
056
057   private final HttpPartType partType;
058   private final String name;
059   private final RestRequest request;
060   private HttpPartParserSession parser;
061   private HttpPartSchema schema;
062   String value;
063
064   /**
065    * Constructor.
066    *
067    * @param partType The HTTP part type.
068    * @param request The request object.
069    * @param name The part name.
070    * @param value The part value.
071    */
072   public RequestHttpPart(HttpPartType partType, RestRequest request, String name, String value) {
073      this.partType = partType;
074      this.request = request;
075      this.name = name;
076      this.value = value;
077      parser(null);
078   }
079
080   //------------------------------------------------------------------------------------------------------------------
081   // Setters
082   //------------------------------------------------------------------------------------------------------------------
083
084   /**
085    * Specifies the part schema for this part.
086    *
087    * <p>
088    * Used by schema-based part parsers such as {@link OpenApiParser}.
089    *
090    * @param value
091    *    The part schema.
092    * @return This object.
093    */
094   public RequestHttpPart schema(HttpPartSchema value) {
095      this.schema = value;
096      return this;
097   }
098
099   /**
100    * Specifies the part parser to use for this part.
101    *
102    * <p>
103    * If not specified, uses the part parser defined on the client by calling {@link org.apache.juneau.rest.RestContext.Builder#partParser()}.
104    *
105    * @param value
106    *    The new part parser to use for this part.
107    *    <br>If <jk>null</jk>, {@link SimplePartParser#DEFAULT} will be used.
108    * @return This object.
109    */
110   public RequestHttpPart parser(HttpPartParserSession value) {
111      this.parser = value == null ? SimplePartParser.DEFAULT_SESSION : value;
112      return this;
113   }
114
115   /**
116    * Sets a default value for this part.
117    *
118    * @param def The default value.
119    * @return This object.
120    */
121   public RequestHttpPart def(String def) {
122      if (value == null)
123         value = def;
124      return this;
125   }
126
127   //------------------------------------------------------------------------------------------------------------------
128   // Retrievers
129   //------------------------------------------------------------------------------------------------------------------
130
131   /**
132    * Returns the value of this part.
133    *
134    * @return The value of this part.
135    */
136   public String getValue() {
137      return value;
138   }
139
140   /**
141    * Returns <jk>true</jk> if this part exists on the request.
142    *
143    * <p>
144    * This is a shortened form for calling <c>asString().isPresent()</c>.
145    *
146    * @return <jk>true</jk> if this part exists on the request.
147    */
148   public boolean isPresent() {
149      return asString().isPresent();
150   }
151
152   /**
153    * If a value is present, returns the value, otherwise throws {@link NoSuchElementException}.
154    *
155    * <p>
156    * This is a shortened form for calling <c>asString().get()</c>.
157    *
158    * @return The value if present.
159    */
160   public String get() {
161      return asString().get();
162   }
163
164   /**
165    * Return the value if present, otherwise return other.
166    *
167    * <p>
168    * This is a shortened form for calling <c>asString().orElse(<jv>other</jv>)</c>.
169    *
170    * @param other The value to be returned if there is no value present, may be <jk>null</jk>.
171    * @return The value, if present, otherwise other.
172    */
173   public String orElse(String other) {
174      return asString().orElse(other);
175   }
176
177   /**
178    * Returns the value of this part as a string.
179    *
180    * @return The value of this part as a string, or {@link Optional#empty()} if the part was not present.
181    */
182   public Optional<String> asString() {
183      return Utils.opt(getValue());
184   }
185
186   /**
187    * Converts this part to the specified POJO type using the request {@link HttpPartParser} and optional schema.
188    *
189    * <p>
190    * See <a class="doclink" href="https://juneau.apache.org/docs/topics/ComplexDataTypes">Complex Data Types</a> for information on defining complex generic types of {@link Map Maps} and {@link Collection Collections}.
191    *
192    * @param <T> The type to convert to.
193    * @param type The type to convert to.
194    * @param args The type parameters.
195    * @return The converted type, or {@link Optional#empty()} if the part is not present.
196    * @throws BasicHttpException If value could not be parsed.
197    */
198   public <T> Optional<T> as(Type type, Type...args) throws BasicHttpException {
199      return as(request.getBeanSession().getClassMeta(type, args));
200   }
201
202   /**
203    * Converts this part to the specified POJO type using the request {@link HttpPartParser} and optional schema.
204    *
205    * <p>
206    * If the specified type is an HTTP part type (extends from {@link org.apache.http.Header}/{@link NameValuePair}), then looks for
207    * one of the following constructors:
208    * <ul class='javatree'>
209    *    <li class='jm><c><jk>public</jk> T(String <jv>value</jv>);</c>
210    *    <li class='jm><c><jk>public</jk> T(String <jv>name</jv>, String <jv>value</jv>);</c>
211    * </ul>
212    *
213    * <p>
214    * If it doesn't find one of those constructors, then it parses it into the specified type using the part parser.
215    *
216    * @param <T> The type to convert to.
217    * @param type The type to convert to.
218    * @return The converted type, or {@link Optional#empty()} if the part is not present.
219    * @throws BasicHttpException If value could not be parsed.
220    */
221   public <T> Optional<T> as(Class<T> type) throws BasicHttpException {
222      return as(request.getBeanSession().getClassMeta(type));
223   }
224
225   /**
226    * Converts this part to the specified POJO type using the request {@link HttpPartParser} and optional schema.
227    *
228    * <p>
229    * If the specified type is an HTTP part type (extends from {@link org.apache.http.Header}/{@link NameValuePair}), then looks for
230    * one of the following constructors:
231    * <ul class='javatree'>
232    *    <li class='jm><c><jk>public</jk> T(String <jv>value</jv>);</c>
233    *    <li class='jm><c><jk>public</jk> T(String <jv>name</jv>, String <jv>value</jv>);</c>
234    * </ul>
235    *
236    * <p>
237    * If it doesn't find one of those constructors, then it parses it into the specified type using the part parser.
238    *
239    * @param <T> The type to convert to.
240    * @param type The type to convert to.
241    * @return The converted type, or {@link Optional#empty()} if the part is not present.
242    * @throws BasicHttpException If value could not be parsed.
243    */
244   public <T> Optional<T> as(ClassMeta<T> type) throws BasicHttpException {
245      try {
246         if (HttpParts.isHttpPart(partType, type)) {
247            ConstructorInfo cc = HttpParts.getConstructor(type).orElse(null);
248            if (cc != null) {
249               if (! isPresent())
250                  return Utils.opte();
251               if (cc.hasParamTypes(String.class))
252                  return Utils.opt(cc.invoke(get()));
253               if (cc.hasParamTypes(String.class, String.class))
254                  return Utils.opt(cc.invoke(getName(), get()));
255            }
256         }
257         return Utils.opt(parser.parse(HEADER, schema, orElse(null), type));
258      } catch (ParseException e) {
259         throw new BadRequest(e, "Could not parse {0} parameter ''{1}''.", partType.toString().toLowerCase(), getName());
260      }
261   }
262
263   /**
264    * Matches the specified pattern against this part value.
265    *
266    * <h5 class='section'>Example:</h5>
267    * <p class='bjava'>
268    *    Matcher <jv>matcher</jv> = <jv>request</jv>
269    *       .getHeader(<js>"Content-Type"</js>)
270    *       .asMatcher(Pattern.<jsm>compile</jsm>(<js>"application/(.*)"</js>));
271    *
272    * <jk>if</jk> (<jv>matcher</jv>.matches()) {
273    *       String <jv>mediaType</jv> = <jv>matcher</jv>.group(1);
274    *    }
275    * </p>
276    *
277    * @param pattern The regular expression pattern to match.
278    * @return The matcher.
279    * @throws BasicHttpException If a connection error occurred.
280    */
281   public Matcher asMatcher(Pattern pattern) throws BasicHttpException {
282      return pattern.matcher(orElse(""));
283   }
284
285   /**
286    * Matches the specified pattern against this part value.
287    *
288    * <h5 class='section'>Example:</h5>
289    * <p class='bjava'>
290    *    Matcher <jv>matcher</jv> = <jv>request</jv>
291    *       .getHeader(<js>"Content-Type"</js>)
292    *       .asMatcher(<js>"application/(.*)"</js>);
293    *
294    *    <jk>if</jk> (<jv>matcher</jv>.matches()) {
295    *       String <jv>mediaType</jv> = <jv>matcher</jv>.group(1);
296    *    }
297    * </p>
298    *
299    * @param regex The regular expression pattern to match.
300    * @return The matcher.
301    * @throws BasicHttpException If a connection error occurred.
302    */
303   public Matcher asMatcher(String regex) throws BasicHttpException {
304      return asMatcher(regex, 0);
305   }
306
307   /**
308    * Matches the specified pattern against this part value.
309    *
310    * <h5 class='section'>Example:</h5>
311    * <p class='bjava'>
312    *    Matcher <jv>matcher</jv> = <jv>request</jv>
313    *       .getHeader(<js>"Content-Type"</js>)
314    *       .asMatcher(<js>"application/(.*)"</js>, <jsf>CASE_INSENSITIVE</jsf>);
315    *
316    *    <jk>if</jk> (<jv>matcher</jv>.matches()) {
317    *       String <jv>mediaType</jv> = <jv>matcher</jv>.group(1);
318    *    }
319    * </p>
320    *
321    * @param regex The regular expression pattern to match.
322    * @param flags Pattern match flags.  See {@link Pattern#compile(String, int)}.
323    * @return The matcher.
324    * @throws BasicHttpException If a connection error occurred.
325    */
326   public Matcher asMatcher(String regex, int flags) throws BasicHttpException {
327      return asMatcher(Pattern.compile(regex, flags));
328   }
329
330   /**
331    * Returns the value of this parameter as an integer.
332    *
333    * @return The value of this parameter as an integer, or {@link Optional#empty()} if the parameter was not present.
334    */
335   public Optional<Integer> asInteger() {
336      return asIntegerPart().asInteger();
337   }
338
339   /**
340    * Returns the value of this parameter as a boolean.
341    *
342    * @return The value of this parameter as a boolean, or {@link Optional#empty()} if the parameter was not present.
343    */
344   public Optional<Boolean> asBoolean() {
345      return asBooleanPart().asBoolean();
346   }
347
348   /**
349    * Returns the value of this parameter as a long.
350    *
351    * @return The value of this parameter as a long, or {@link Optional#empty()} if the parameter was not present.
352    */
353   public Optional<Long> asLong() {
354      return asLongPart().asLong();
355   }
356
357   /**
358    * Returns the value of this parameter as a date.
359    *
360    * @return The value of this parameter as a date, or {@link Optional#empty()} if the parameter was not present.
361    */
362   public Optional<ZonedDateTime> asDate() {
363      return asDatePart().asZonedDateTime();
364   }
365
366   /**
367    * Returns the value of this parameter as a list from a comma-delimited string.
368    *
369    * @return The value of this parameter as a list from a comma-delimited string, or {@link Optional#empty()} if the parameter was not present.
370    */
371   public Optional<List<String>> asCsvArray() {
372      return asCsvArrayPart().asList();
373   }
374
375   /**
376    * Returns the value of this parameter as a {@link BasicCsvArrayPart}.
377    *
378    * @return The value of this parameter as a {@link BasicCsvArrayPart}, never <jk>null</jk>.
379    */
380   public BasicCsvArrayPart asCsvArrayPart() {
381      return new BasicCsvArrayPart(getName(), getValue());
382   }
383
384   /**
385    * Returns the value of this parameter as a {@link BasicDatePart}.
386    *
387    * @return The value of this parameter as a {@link BasicDatePart}, never <jk>null</jk>.
388    */
389   public BasicDatePart asDatePart() {
390      return new BasicDatePart(getName(), getValue());
391   }
392
393   /**
394    * Returns the value of this parameter as a {@link BasicIntegerPart}.
395    *
396    * @return The value of this parameter as a {@link BasicIntegerPart}, never <jk>null</jk>.
397    */
398   public BasicIntegerPart asIntegerPart() {
399      return new BasicIntegerPart(getName(), getValue());
400   }
401
402   /**
403    * Returns the value of this parameter as a {@link BasicBooleanPart}.
404    *
405    * @return The value of this parameter as a {@link BasicBooleanPart}, never <jk>null</jk>.
406    */
407   public BasicBooleanPart asBooleanPart() {
408      return new BasicBooleanPart(getName(), getValue());
409   }
410
411   /**
412    * Returns the value of this parameter as a {@link BasicLongPart}.
413    *
414    * @return The value of this parameter as a {@link BasicLongPart}, never <jk>null</jk>.
415    */
416   public BasicLongPart asLongPart() {
417      return new BasicLongPart(getName(), getValue());
418   }
419
420   /**
421    * Returns the value of this parameter as a {@link BasicStringPart}.
422    *
423    * @return The value of this parameter as a {@link BasicStringPart}, never <jk>null</jk>.
424    */
425   public BasicStringPart asStringPart() {
426      return new BasicStringPart(getName(), getValue());
427   }
428
429   /**
430    * Returns the value of this parameter as a {@link BasicUriPart}.
431    *
432    * @return The value of this parameter as a {@link BasicUriPart}, never <jk>null</jk>.
433    */
434   public BasicUriPart asUriPart() {
435      return new BasicUriPart(getName(), getValue());
436   }
437
438   //------------------------------------------------------------------------------------------------------------------
439   // Assertions
440   //------------------------------------------------------------------------------------------------------------------
441
442   /**
443    * Provides the ability to perform fluent-style assertions on this parameter.
444    *
445    * <h5 class='section'>Examples:</h5>
446    * <p class='bjava'>
447    *    <jv>request</jv>
448    *       .getQueryParam(<js>"foo"</js>)
449    *       .assertString().contains(<js>"bar"</js>);
450    * </p>
451    *
452    * <p>
453    * The assertion test returns the original object allowing you to chain multiple requests like so:
454    * <p class='bjava'>
455    *    String <jv>foo</jv> = <jv>request</jv>
456    *       .getQueryParam(<js>"foo"</js>)
457    *       .assertString().contains(<js>"bar"</js>)
458    *       .asString().get();
459    * </p>
460    *
461    * @return A new fluent assertion object.
462    */
463   public FluentStringAssertion<RequestHttpPart> assertString() {
464      return new FluentStringAssertion<>(orElse(null), this);
465   }
466
467   /**
468    * Provides the ability to perform fluent-style assertions on an integer parameter.
469    *
470    * <h5 class='section'>Examples:</h5>
471    * <p class='bjava'>
472    *    <jv>request</jv>
473    *       .getQueryParam(<js>"age"</js>)
474    *       .assertInteger().isGreaterThan(1);
475    * </p>
476    *
477    * @return A new fluent assertion object.
478    */
479   public FluentIntegerAssertion<RequestHttpPart> assertInteger() {
480      return new FluentIntegerAssertion<>(asIntegerPart().asInteger().orElse(null), this);
481   }
482
483   /**
484    * Provides the ability to perform fluent-style assertions on a long parameter.
485    *
486    * <h5 class='section'>Examples:</h5>
487    * <p class='bjava'>
488    *    <jv>request</jv>
489    *       .getQueryParam(<js>"length"</js>)
490    *       .assertLong().isLessThan(100000);
491    * </p>
492    *
493    * @return A new fluent assertion object.
494    */
495   public FluentLongAssertion<RequestHttpPart> assertLong() {
496      return new FluentLongAssertion<>(asLongPart().asLong().orElse(null), this);
497   }
498
499   /**
500    * Provides the ability to perform fluent-style assertions on a date parameter.
501    *
502    * <h5 class='section'>Examples:</h5>
503    * <p class='bjava'>
504    *    <jv>request</jv>
505    *       .getQueryParam(<js>"time"</js>)
506    *       .assertDate().isAfterNow();
507    * </p>
508    *
509    * @return A new fluent assertion object.
510    */
511   public FluentZonedDateTimeAssertion<RequestHttpPart> assertDate() {
512      return new FluentZonedDateTimeAssertion<>(asDatePart().asZonedDateTime().orElse(null), this);
513   }
514
515   /**
516    * Provides the ability to perform fluent-style assertions on comma-separated string parameters.
517    *
518    * <h5 class='section'>Examples:</h5>
519    * <p class='bjava'>
520    *    <jv>request</jv>
521    *       .getQueryParam(<js>"allow"</js>)
522    *       .assertCsvArray().contains(<js>"GET"</js>);
523    * </p>
524    *
525    * @return A new fluent assertion object.
526    */
527   public FluentListAssertion<String,RequestHttpPart> assertCsvArray() {
528      return new FluentListAssertion<>(asCsvArrayPart().asList().orElse(null), this);
529   }
530
531   /**
532    * Returns the request that created this part.
533    *
534    * @return The request that created this part.
535    */
536   protected RestRequest getRequest() {
537      return request;
538   }
539
540   /**
541    * Gets the name of this part.
542    *
543    * @return The name of this part, never <jk>null</jk>.
544    */
545   public String getName() {
546      return name;
547   }
548
549   @Override /* Object */
550   public String toString() {
551      return getName() + "=" + getValue();
552   }
553}