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.http.part;
018
019import static java.time.format.DateTimeFormatter.*;
020import static java.time.temporal.ChronoUnit.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022
023import java.time.*;
024import java.util.*;
025import java.util.function.*;
026
027/**
028 * An {@link org.apache.juneau.http.HttpPart} whose value is an ISO-8601 date-time.
029 *
030 * <p>
031 * Mirrors the semantics of {@code BasicDatePart} from {@code juneau-rest-common-classic} without
032 * the Apache HttpCore dependency. Values are parsed/formatted via
033 * {@link java.time.format.DateTimeFormatter#ISO_DATE_TIME}.
034 *
035 * @since 10.0.0
036 */
037@SuppressWarnings({
038   "java:S2160" // Equality is fully determined by name + getValue() in HttpPartBean; the typed fields are derived from the wire value and add no state to compare
039})
040public class HttpDatePart extends HttpPartBean {
041
042   /**
043    * Creates an {@link HttpDatePart} with the given name and date value.
044    *
045    * @param name The part name. Must not be <jk>null</jk>.
046    * @param value The date value. May be <jk>null</jk>.
047    * @return A new instance. Never <jk>null</jk>.
048    */
049   public static HttpDatePart of(String name, ZonedDateTime value) {
050      return new HttpDatePart(name, value);
051   }
052
053   /**
054    * Creates an {@link HttpDatePart} by parsing the given ISO-8601 wire value.
055    *
056    * @param name The part name. Must not be <jk>null</jk>.
057    * @param value The wire value. May be <jk>null</jk> or empty.
058    * @return A new instance. Never <jk>null</jk>.
059    */
060   public static HttpDatePart ofString(String name, String value) {
061      return new HttpDatePart(name, value);
062   }
063
064   /**
065    * Creates an {@link HttpDatePart} with a lazy date supplier.
066    *
067    * @param name The part name. Must not be <jk>null</jk>.
068    * @param supplier Supplier for the date value. Must not be <jk>null</jk>.
069    * @return A new instance. Never <jk>null</jk>.
070    */
071   public static HttpDatePart ofLazy(String name, Supplier<ZonedDateTime> supplier) {
072      return new HttpDatePart(name, supplier);
073   }
074
075   private final ZonedDateTime typedValue;
076   private final Supplier<ZonedDateTime> typedSupplier;
077
078   /**
079    * Constructor accepting an ISO-8601 wire-format string value.
080    *
081    * @param name The part name. Must not be <jk>null</jk>.
082    * @param value The wire value. May be <jk>null</jk> or empty.
083    */
084   public HttpDatePart(String name, String value) {
085      super(name, value);
086      this.typedValue = ie(value) ? null : ZonedDateTime.from(ISO_DATE_TIME.parse(value)).truncatedTo(SECONDS);
087      this.typedSupplier = null;
088   }
089
090   /**
091    * Constructor accepting a {@link ZonedDateTime} value.
092    *
093    * @param name The part name. Must not be <jk>null</jk>.
094    * @param value The date value. May be <jk>null</jk>.
095    */
096   public HttpDatePart(String name, ZonedDateTime value) {
097      super(name, value == null ? null : ISO_DATE_TIME.format(value));
098      this.typedValue = value;
099      this.typedSupplier = null;
100   }
101
102   /**
103    * Constructor accepting a lazy {@link ZonedDateTime} supplier.
104    *
105    * @param name The part name. Must not be <jk>null</jk>.
106    * @param supplier Supplier for the date value. Must not be <jk>null</jk>.
107    */
108   public HttpDatePart(String name, Supplier<ZonedDateTime> supplier) {
109      super(name, () -> {
110         var v = supplier.get();
111         return v == null ? null : ISO_DATE_TIME.format(v);
112      });
113      this.typedValue = null;
114      this.typedSupplier = supplier;
115   }
116
117   /**
118    * Returns the value as an {@link Optional}{@code <ZonedDateTime>}.
119    *
120    * @return The date value, wrapped in an {@link Optional}. Never <jk>null</jk>.
121    */
122   public Optional<ZonedDateTime> asZonedDateTime() {
123      return o(toZonedDateTime());
124   }
125
126   /**
127    * Returns the value if present, otherwise returns {@code other}.
128    *
129    * @param other The default value.
130    * @return The value or {@code other}.
131    */
132   public ZonedDateTime orElse(ZonedDateTime other) {
133      var x = toZonedDateTime();
134      return nn(x) ? x : other;
135   }
136
137   /**
138    * Returns the value as a {@link ZonedDateTime}, or {@code null} if not set.
139    *
140    * @return The date value, possibly {@code null}.
141    */
142   public ZonedDateTime toZonedDateTime() {
143      if (typedSupplier != null)
144         return typedSupplier.get();
145      return typedValue;
146   }
147}