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.header;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.io.*;
022import java.util.*;
023import java.util.function.*;
024
025import org.apache.http.*;
026import org.apache.http.message.*;
027import org.apache.juneau.commons.utils.*;
028import org.apache.juneau.marshall.*;
029import org.apache.juneau.bct.assertions.*;
030
031/**
032 * Superclass of all headers defined in this package.
033 *
034 * Provides the following features:
035 * <ul class='spaced-list'>
036 *    <li>
037 *       Default support for various streams and readers.
038 *    <li>
039 *       Content from {@link Supplier Suppliers}.
040 *    <li>
041 *       Caching.
042 *    <li>
043 *       Fluent setters.
044 *    <li>
045 *       Fluent assertions.
046 * </ul>
047 *
048 * <h5 class='section'>See Also:</h5><ul>
049 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
050 *    <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
051 * </ul>
052 *
053 * @serial exclude
054 */
055@Marshalled(as=MarshalledAs.STRING)
056public class BasicHeader implements Header, Serializable {
057   private static final long serialVersionUID = 1L;
058   private static final HeaderElement[] EMPTY_HEADER_ELEMENTS = {};
059
060   /**
061    * Static creator.
062    *
063    * @param o
064    *    The name value pair that makes up the header name and value.
065    *    <br>Must not be <jk>null</jk>.
066    * @return A new header bean.
067    */
068   public static BasicHeader of(NameValuePair o) {
069      return new BasicHeader(o.getName(), o.getValue());
070   }
071
072   /**
073    * Static creator.
074    *
075    * @param name The parameter name.
076    * @param value
077    *    The parameter value.
078    *    <br>Can be <jk>null</jk>.
079    * @return A new header bean, or <jk>null</jk> if value is <jk>null</jk>.
080    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
081    */
082   public static BasicHeader of(String name, Object value) {
083      return value == null ? null : new BasicHeader(name, value);
084   }
085
086   private final String name;
087   private final String stringValue;
088
089   private final transient Object value;
090   private final transient Supplier<Object> supplier;
091
092   private transient HeaderElement[] elements;
093
094   /**
095    * Constructor.
096    *
097    * @param name The parameter name.
098    * @param value
099    *    The parameter value.
100    *    <br>Any non-String value will be converted to a String using {@link Object#toString()}.
101    *    <br>Can also be an <l>Object</l> {@link Supplier}.
102    *    <br>Can be <jk>null</jk> to create a header with no value.
103    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
104    */
105   @SuppressWarnings({
106      "unchecked" // Type erasure requires cast for supplier
107   })
108   public BasicHeader(String name, Object value) {
109      req(Shorts.ine(name), "Name cannot be empty on header.");  // NOAI
110      this.name = name;
111      this.value = value instanceof Supplier ? null : value;
112      this.stringValue = s(value);
113      this.supplier = cast(Supplier.class, value);
114   }
115
116   /**
117    * Constructor with delayed value.
118    *
119    * <p>
120    * Header value is re-evaluated on each call to {@link #getValue()}.
121    *
122    * @param name The header name.
123    * @param value
124    *    The supplier of the header value.
125    *    <br>Can be <jk>null</jk> to create a header with no value.
126    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
127    */
128   public BasicHeader(String name, Supplier<Object> value) {
129      req(Shorts.ine(name), "Name cannot be empty on header.");  // NOAI
130      this.name = name;
131      this.value = null;
132      this.stringValue = null;
133      supplier = value;
134   }
135
136   /**
137    * Copy constructor.
138    *
139    * @param copyFrom The object to copy.  Must not be <jk>null</jk>.
140    */
141   protected BasicHeader(BasicHeader copyFrom) {
142      this.name = copyFrom.name;
143      this.value = copyFrom.value;
144      this.stringValue = copyFrom.stringValue;
145      this.supplier = copyFrom.supplier;
146      if (copyFrom.elements != null) {
147         this.elements = cp(copyFrom.elements);
148      }
149   }
150
151   /**
152    * Creates a copy of this header.
153    *
154    * @return A new header instance with copied values.
155    */
156   public BasicHeader copy() {
157      return new BasicHeader(this);
158   }
159
160   /**
161    * Provides an object for performing assertions against the name of this header.
162    *
163    * @return An object for performing assertions against the name of this header.
164    */
165   public FluentStringAssertion<BasicHeader> assertName() {
166      return new FluentStringAssertion<>(getName(), this);
167   }
168
169   /**
170    * Provides an object for performing assertions against the value of this header.
171    *
172    * @return An object for performing assertions against the value of this header.
173    */
174   public FluentStringAssertion<BasicHeader> assertStringValue() {
175      return new FluentStringAssertion<>(getValue(), this);
176   }
177
178   /**
179    * Returns the value of this header as a string.
180    *
181    * @return The value of this header as a string, or {@link Optional#empty()} if the value is <jk>null</jk>
182    */
183   public Optional<String> asString() {
184      return o(getValue());
185   }
186
187   @Override /* Overridden from Object */
188   public boolean equals(Object o) {
189      // Functionality provided for HttpRequest.removeHeader().
190      // Not a perfect equality operator if using SVL vars.
191      return o instanceof Header o2 && eq(this, o2, (x, y) -> eq(x.name, y.getName()) && eq(x.getValue(), y.getValue()));
192   }
193
194   /**
195    * Returns <jk>true</jk> if the specified value is the same using {@link String#equalsIgnoreCase(String)}.
196    *
197    * @param compare The value to compare against.
198    * @return <jk>true</jk> if the specified value is the same.
199    */
200   public boolean equalsIgnoreCase(String compare) {
201      return eqic(getValue(), compare);
202   }
203
204   /**
205    * If a value is present, returns the value, otherwise throws {@link NoSuchElementException}.
206    *
207    * <p>
208    * This is a shortcut for calling <c>asString().get()</c>.
209    *
210    * @return The value if present.
211    */
212   public String get() {
213      return asString().orElseThrow(() -> new NoSuchElementException("Header value is not present"));
214   }
215
216   @Override
217   public HeaderElement[] getElements() throws ParseException {
218      if (elements == null) {
219         var s = getValue();
220         HeaderElement[] x = s == null ? EMPTY_HEADER_ELEMENTS : BasicHeaderValueParser.parseElements(s, null);
221         if (supplier == null)
222            elements = x;
223         return cp(x);
224      }
225      return cp(elements);
226   }
227
228   @Override /* Overridden from Header */
229   public String getName() { return name; }
230
231   @Override /* Overridden from Header */
232   public String getValue() {
233      if (nn(supplier))
234         return s(supplier.get());
235      return stringValue;
236   }
237
238   @Override /* Overridden from Object */
239   public int hashCode() {
240      // Must be consistent with equals(Object), which compares name + value (see BUG-03 / 10.0.0 release note).
241      return h(name, getValue());
242   }
243
244   /**
245    * Returns <jk>true</jk> if the value exists and is not empty.
246    *
247    * <p>
248    * This is a shortcut for calling <c>!asString().orElse(<js>""</js>).isEmpty()</c>.
249    *
250    * @return <jk>true</jk> if the value exists and is not empty.
251    */
252   public boolean isNotEmpty() { return ! asString().orElse("").isEmpty(); }
253
254   /**
255    * Returns <jk>true</jk> if the value exists.
256    *
257    * <p>
258    * This is a shortcut for calling <c>asString().isPresent()</c>.
259    *
260    * @return <jk>true</jk> if the value exists.
261    */
262   public boolean isPresent() { return asString().isPresent(); }
263
264   /**
265    * If a value is present, returns the value, otherwise returns other.
266    *
267    * <p>
268    * This is a shortcut for calling <c>asString().orElse(<jv>other</jv>)</c>.
269    *
270    * @param other The other value.
271    * @return The value if present or the other value if not.
272    */
273   public String orElse(String other) {
274      return asString().orElse(other);
275   }
276
277   @Override /* Overridden from Object */
278   public String toString() {
279      return getName() + ": " + getValue();
280   }
281}