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