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.*;
020import static org.apache.juneau.commons.utils.StringUtils.*;
021
022import java.util.*;
023import java.util.function.*;
024
025import org.apache.juneau.bct.assertions.*;
026
027/**
028 * Category of headers that consist of a comma-delimited list of string values.
029 *
030 * <p>
031 * <h5 class='figure'>Example</h5>
032 * <p class='bcode'>
033 *    Allow: GET, PUT
034 * </p>
035 *
036 * <h5 class='section'>See Also:</h5><ul>
037 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
038 *    <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
039 * </ul>
040 *
041 * @serial exclude
042 */
043@SuppressWarnings({
044   "java:S2160", // equals() inherited from BasicHeader compares name+value; typed field is accessed via getValue()
045   "java:S3776" // contains()/containsIgnoreCase() check both the fixed value array and the supplier with nested null-guarded loops
046})
047public class BasicCsvHeader extends BasicHeader {
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 a comma-delimited list.
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 BasicCsvHeader of(String name, String value) {
062      return value == null ? null : new BasicCsvHeader(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 BasicCsvHeader of(String name, String...value) {
076      return value == null ? null : new BasicCsvHeader(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 BasicCsvHeader of(String name, Supplier<String[]> value) {
093      return value == null ? null : new BasicCsvHeader(name, value);
094   }
095
096   private final String[] value;
097   private final transient Supplier<String[]> supplier;
098
099   /**
100    * Constructor.
101    *
102    * @param name The header name.
103    * @param value
104    *    The header value.
105    *    <br>Must be a comma-delimited list.
106    *    <br>Can be <jk>null</jk> to create a header with no value.
107    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
108    */
109   public BasicCsvHeader(String name, String value) {
110      super(name, value);
111      this.value = splita(value);
112      this.supplier = null;
113   }
114
115   /**
116    * Constructor.
117    *
118    * @param name The header name.
119    * @param value
120    *    The header value.
121    *    <br>Can be <jk>null</jk> to create a header with no value.
122    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
123    */
124   public BasicCsvHeader(String name, String...value) {
125      super(name, join(value, ", "));
126      this.value = cp(value);
127      this.supplier = null;
128   }
129
130   /**
131    * Constructor with delayed value.
132    *
133    * <p>
134    * Header value is re-evaluated on each call to {@link #getValue()}.
135    *
136    * @param name The header name.
137    * @param value
138    *    The supplier of the header value.
139    *    <br>Can be <jk>null</jk> to create a header with no value.
140    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
141    */
142   public BasicCsvHeader(String name, Supplier<String[]> value) {
143      super(name, null);
144      this.value = null;
145      supplier = value;
146   }
147
148   /**
149    * Returns the header value as an array wrapped in an {@link Optional}.
150    *
151    * <p>
152    * The array is a copy of the value of this header.
153    *
154    * @return The header value as an array wrapped in an {@link Optional}.  Never <jk>null</jk>.
155    */
156   public Optional<String[]> asArray() {
157      return o(cp(value()));
158   }
159
160   /**
161    * Returns the header value as a list wrapped in an {@link Optional}.
162    *
163    * <p>
164    * The list is unmodifiable.
165    *
166    * @return The header value as a list wrapped in an {@link Optional}.  Never <jk>null</jk>.
167    */
168   public Optional<List<String>> asList() {
169      return o(u(l(value())));
170   }
171
172   /**
173    * Provides the ability to perform fluent-style assertions on this header.
174    *
175    * @return A new fluent assertion object.
176    * @throws AssertionError If assertion failed.
177    */
178   public FluentListAssertion<String,BasicCsvHeader> assertList() {
179      return new FluentListAssertion<>(u(l(value())), this);
180   }
181
182   /**
183    * Returns <jk>true</jk> if this header contains the specified value.
184    *
185    * @param val The value to check for.
186    * @return <jk>true</jk> if this header contains the specified value.
187    */
188   public boolean contains(String val) {
189      if (nn(value))
190         for (var v : value)
191            if (eq(v, val))
192               return true;
193      if (nn(supplier)) {
194         String[] value2 = supplier.get();
195         if (nn(value2))
196            for (var v : value2)
197               if (eq(v, val))
198                  return true;
199
200      }
201      return false;
202   }
203
204   /**
205    * Returns <jk>true</jk> if this header contains the specified value using {@link String#equalsIgnoreCase(String)}.
206    *
207    * @param val The value to check for.
208    * @return <jk>true</jk> if this header contains the specified value.
209    */
210   public boolean containsIgnoreCase(String val) {
211      if (nn(value))
212         for (var v : value)
213            if (eqic(v, val))
214               return true;
215      if (nn(supplier)) {
216         String[] value2 = supplier.get();
217         if (nn(value2))
218            for (var v : value2)
219               if (eqic(v, val))
220                  return true;
221      }
222      return false;
223   }
224
225   @Override /* Overridden from Header */
226   public String getValue() { return join(value(), ", "); }
227
228   /**
229    * Return the value if present, otherwise return <c>other</c>.
230    *
231    * <p>
232    * This is a shortened form for calling <c>asArray().orElse(<jv>other</jv>)</c>.
233    *
234    * @param other The value to be returned if there is no value present, can be <jk>null</jk>.
235    * @return The value, if present, otherwise <c>other</c>.
236    */
237   public String[] orElse(String[] other) {
238      String[] x = value();
239      return nn(x) ? cp(x) : other;
240   }
241
242   /**
243    * Returns the header value as an array.
244    *
245    * <p>
246    * The array is a copy of the value of this header.
247    *
248    * @return The header value as an array, or <jk>null</jk> if the header has no value.
249    */
250   public String[] toArray() {
251      return cp(value());
252   }
253
254   /**
255    * Returns the header value as a list.
256    *
257    * <p>
258    * The list is unmodifiable.
259    *
260    * @return The header value as a list, or <jk>null</jk> if the header has no value.
261    */
262   public List<String> toList() {
263      return u(l(value()));
264   }
265
266   private String[] value() {
267      if (nn(supplier))
268         return supplier.get();
269      return value;
270   }
271}