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.classic.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.time.format.*;
025import java.util.*;
026import java.util.function.*;
027
028import org.apache.http.*;
029import org.apache.juneau.bct.assertions.*;
030
031/**
032 * A {@link NameValuePair} that consist of a single HTTP-date.
033 *
034 * <h5 class='section'>See Also:</h5><ul>
035 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
036 * </ul>
037 */
038@SuppressWarnings({
039   "java:S2160" // equals() inherited from BasicPart compares name+value; typed ZonedDateTime field is accessed via getValue()
040})
041public class BasicDatePart extends BasicPart {
042
043   /**
044    * Static creator with delayed value.
045    *
046    * <p>
047    * Part value is re-evaluated on each call to {@link NameValuePair#getValue()}.
048    *
049    * @param name The part name.
050    * @param value The part value supplier.
051    * @return A new {@link BasicDatePart} object, or <jk>null</jk> if the name or supplier is <jk>null</jk>.
052    */
053   public static BasicDatePart of(String name, Supplier<ZonedDateTime> value) {
054      if (ie(name) || value == null)
055         return null;
056      return new BasicDatePart(name, value);
057   }
058
059   /**
060    * Static creator.
061    *
062    * @param name The part name.
063    * @param value The part value.
064    * @return A new {@link BasicDatePart} object, or <jk>null</jk> if the name or value is <jk>null</jk>.
065    */
066   public static BasicDatePart of(String name, ZonedDateTime value) {
067      if (ie(name) || value == null)
068         return null;
069      return new BasicDatePart(name, value);
070   }
071
072   private final ZonedDateTime value;
073   private final Supplier<ZonedDateTime> supplier;
074
075   /**
076    * Constructor.
077    *
078    * <p>
079    * <jk>null</jk> and empty values are treated as <jk>null</jk>.
080    * Otherwise parses using {@link DateTimeFormatter#ISO_DATE_TIME}.
081    *
082    * @param name The part name.  Must not be <jk>null</jk>.
083    * @param value The part value.  Can be <jk>null</jk>.
084    */
085   public BasicDatePart(String name, String value) {
086      super(name, value);
087      this.value = ie(value) ? null : ZonedDateTime.from(ISO_DATE_TIME.parse(value)).truncatedTo(SECONDS);
088      this.supplier = null;
089   }
090
091   /**
092    * Constructor.
093    *
094    * @param name The part name.  Must not be <jk>null</jk>.
095    * @param value The part value supplier.  Can be <jk>null</jk> or supply <jk>null</jk>.
096    */
097   public BasicDatePart(String name, Supplier<ZonedDateTime> value) {
098      super(name, value);
099      this.value = null;
100      supplier = value;
101   }
102
103   /**
104    * Constructor.
105    *
106    * @param name The part name.  Must not be <jk>null</jk>.
107    * @param value The part value.  Can be <jk>null</jk>.
108    */
109   public BasicDatePart(String name, ZonedDateTime value) {
110      super(name, value);
111      this.value = value;
112      this.supplier = null;
113   }
114
115   /**
116    * Provides the ability to perform fluent-style assertions on this part.
117    *
118    * @return A new fluent assertion object.
119    * @throws AssertionError If assertion failed.
120    */
121   public FluentZonedDateTimeAssertion<BasicDatePart> assertZonedDateTime() {
122      return new FluentZonedDateTimeAssertion<>(value(), this);
123   }
124
125   /**
126    * Returns The part value as a {@link ZonedDateTime} wrapped in an {@link Optional}.
127    *
128    * @return The part value as a {@link ZonedDateTime} wrapped in an {@link Optional}.  Never <jk>null</jk>.
129    */
130   public Optional<ZonedDateTime> asZonedDateTime() {
131      return o(toZonedDateTime());
132   }
133
134   @Override /* Overridden from Header */
135   public String getValue() {
136      ZonedDateTime v = value();
137      return v == null ? null : ISO_DATE_TIME.format(v);
138   }
139
140   /**
141    * Return the value if present, otherwise return <c>other</c>.
142    *
143    * <p>
144    * This is a shortened form for calling <c>asZonedDateTime().orElse(<jv>other</jv>)</c>.
145    *
146    * @param other The value to be returned if there is no value present, can be <jk>null</jk>.
147    * @return The value, if present, otherwise <c>other</c>.
148    */
149   public ZonedDateTime orElse(ZonedDateTime other) {
150      ZonedDateTime x = value();
151      return nn(x) ? x : other;
152   }
153
154   /**
155    * Returns The part value as a {@link ZonedDateTime}.
156    *
157    * @return The part value as a {@link ZonedDateTime}, or <jk>null</jk> if the value <jk>null</jk>.
158    */
159   public ZonedDateTime toZonedDateTime() {
160      return value();
161   }
162
163   private ZonedDateTime value() {
164      if (nn(supplier))
165         return supplier.get();
166      return value;
167   }
168}