001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.http.header;
014
015import static java.time.format.DateTimeFormatter.*;
016import static java.time.temporal.ChronoUnit.*;
017import static org.apache.juneau.common.internal.StringUtils.*;
018import static org.apache.juneau.internal.CollectionUtils.*;
019
020import java.time.*;
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.assertions.*;
025
026/**
027 * Category of headers that consist of a single HTTP-date.
028 *
029 * <p>
030 * <h5 class='figure'>Example</h5>
031 * <p class='bcode'>
032 *    If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
033 * </p>
034 *
035 * <h5 class='section'>See Also:</h5><ul>
036 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a>
037 *    <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
038 * </ul>
039 *
040 * @serial exclude
041 */
042public class BasicDateHeader extends BasicHeader {
043
044   //-----------------------------------------------------------------------------------------------------------------
045   // Static
046   //-----------------------------------------------------------------------------------------------------------------
047
048   private static final long serialVersionUID = 1L;
049
050   /**
051    * Static creator.
052    *
053    * @param name The header name.
054    * @param value
055    *    The header value.
056    *    <br>Must be an RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>).
057    *    <br>Can be <jk>null</jk>.
058    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
059    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
060    */
061   public static BasicDateHeader of(String name, String value) {
062      return value == null ? null : new BasicDateHeader(name, value);
063   }
064
065   /**
066    * Static creator.
067    *
068    * @param name The header name.
069    * @param value
070    *    The header value.
071    *    <br>Can be <jk>null</jk>.
072    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
073    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
074    */
075   public static BasicDateHeader of(String name, ZonedDateTime value) {
076      return value == null ? null : new BasicDateHeader(name, value);
077   }
078
079   /**
080    * Static creator with delayed value.
081    *
082    * <p>
083    * Header value is re-evaluated on each call to {@link #getValue()}.
084    *
085    * @param name The header name.
086    * @param value
087    *    The supplier of the header value.
088    *    <br>Can be <jk>null</jk>.
089    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
090    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
091    */
092   public static BasicDateHeader of(String name, Supplier<ZonedDateTime> value) {
093      return value == null ? null : new BasicDateHeader(name, value);
094   }
095
096   //-----------------------------------------------------------------------------------------------------------------
097   // Instance
098   //-----------------------------------------------------------------------------------------------------------------
099
100   private final ZonedDateTime value;
101   private final Supplier<ZonedDateTime> supplier;
102
103   /**
104    * Constructor.
105    *
106    * @param name The header name.
107    * @param value
108    *    The header value.
109    *    <br>Must be an RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>).
110    *    <br>Can be <jk>null</jk>.
111    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
112    */
113   public BasicDateHeader(String name, String value) {
114      super(name, value);
115      this.value = isEmpty(value) ? null : ZonedDateTime.from(RFC_1123_DATE_TIME.parse(value.toString())).truncatedTo(SECONDS);
116      this.supplier = null;
117   }
118
119   /**
120    * Constructor.
121    *
122    * @param name The header name.
123    * @param value
124    *    The header value.
125    *    <br>Can be <jk>null</jk>.
126    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
127    */
128   public BasicDateHeader(String name, ZonedDateTime value) {
129      super(name, value);
130      this.value = value;
131      this.supplier = null;
132   }
133
134   /**
135    * Constructor with delayed value.
136    *
137    * <p>
138    * Header value is re-evaluated on each call to {@link #getValue()}.
139    *
140    * @param name The header name.
141    * @param value
142    *    The supplier of the header value.
143    *    <br>Can be <jk>null</jk>.
144    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
145    */
146   public BasicDateHeader(String name, Supplier<ZonedDateTime> value) {
147      super(name, null);
148      this.value = null;
149      this.supplier = value;
150   }
151
152   @Override /* Header */
153   public String getValue() {
154      ZonedDateTime x = value();
155      return x == null ? null : RFC_1123_DATE_TIME.format(x);
156   }
157
158   /**
159    * Returns the header value as a {@link ZonedDateTime} wrapped in an {@link Optional}.
160    *
161    * @return The header value as a {@link ZonedDateTime} wrapped in an {@link Optional}.  Never <jk>null</jk>.
162    */
163   public Optional<ZonedDateTime> asZonedDateTime() {
164      return optional(value());
165   }
166
167   /**
168    * Returns the header value as a {@link ZonedDateTime}.
169    *
170    * @return The header value as a {@link ZonedDateTime}.  Can be <jk>null</jk>.
171    */
172   public ZonedDateTime toZonedDateTime() {
173      return value();
174   }
175
176   /**
177    * Provides the ability to perform fluent-style assertions on this header.
178    *
179    * <h5 class='section'>Examples:</h5>
180    * <p class='bjava'>
181    *    <jc>// Validates the response body content is not expired.</jc>
182    *    <jv>client</jv>
183    *       .get(<jsf>URL</jsf>)
184    *       .run()
185    *       .getHeader(<js>"Expires"</js>).asDateHeader().assertZonedDateTime().isLessThan(<jk>new</jk> Date());
186    * </p>
187    *
188    * @return A new fluent assertion object.
189    * @throws AssertionError If assertion failed.
190    */
191   public FluentZonedDateTimeAssertion<BasicDateHeader> assertZonedDateTime() {
192      return new FluentZonedDateTimeAssertion<>(value(), this);
193   }
194
195   /**
196    * Return the value if present, otherwise return <c>other</c>.
197    *
198    * <p>
199    * This is a shortened form for calling <c>asZonedDateTime().orElse(<jv>other</jv>)</c>.
200    *
201    * @param other The value to be returned if there is no value present, can be <jk>null</jk>.
202    * @return The value, if present, otherwise <c>other</c>.
203    */
204   public ZonedDateTime orElse(ZonedDateTime other) {
205      ZonedDateTime x = value();
206      return x != null ? x : other;
207   }
208
209   private ZonedDateTime value() {
210      if (supplier != null)
211         return supplier.get();
212      return value;
213   }
214}