001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.http.header;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.lang.reflect.*;
018import java.util.*;
019import java.util.function.*;
020
021import org.apache.juneau.assertions.*;
022import org.apache.juneau.collections.*;
023import org.apache.juneau.http.*;
024
025/**
026 * Category of headers that consist of a comma-delimited list of string values.
027 *
028 * <p>
029 * <h5 class='figure'>Example</h5>
030 * <p class='bcode w800'>
031 *    Allow: GET, PUT
032 * </p>
033 *
034 * <ul class='seealso'>
035 *    <li class='extlink'>{@doc ExtRFC2616}
036 * </ul>
037 */
038public class BasicCsvArrayHeader extends BasicHeader {
039
040   private static final long serialVersionUID = 1L;
041
042   /**
043    * Convenience creator.
044    *
045    * @param name The header name.
046    * @param value
047    *    The header value.
048    *    <br>Can be any of the following:
049    *    <ul>
050    *       <li><c>String</c> - A comma-delimited string.
051    *       <li><c>String[]</c> - A pre-parsed value.
052    *       <li>Any other array type - Converted to <c>String[]</c>.
053    *       <li>Any {@link Collection} - Converted to <c>String[]</c>.
054    *       <li>Anything else - Converted to <c>String</c>.
055    *    </ul>
056    * @return A new {@link BasicCsvArrayHeader} object, or <jk>null</jk> if the name or value is <jk>null</jk>.
057    */
058   public static BasicCsvArrayHeader of(String name, Object value) {
059      if (isEmpty(name) || value == null)
060         return null;
061      return new BasicCsvArrayHeader(name, value);
062   }
063
064   /**
065    * Convenience creator using supplier.
066    *
067    * <p>
068    * Header value is re-evaluated on each call to {@link #getValue()}.
069    *
070    * @param name The header name.
071    * @param value
072    *    The header value supplier.
073    *    <br>Can be any of the following:
074    *    <ul>
075    *       <li><c>String</c> - A comma-delimited string.
076    *       <li><c>String[]</c> - A pre-parsed value.
077    *       <li>Any other array type - Converted to <c>String[]</c>.
078    *       <li>Any {@link Collection} - Converted to <c>String[]</c>.
079    *       <li>Anything else - Converted to <c>String</c>.
080    *    </ul>
081    * @return A new {@link BasicCsvArrayHeader} object, or <jk>null</jk> if the name or value is <jk>null</jk>.
082    */
083   public static BasicCsvArrayHeader of(String name, Supplier<?> value) {
084      if (isEmpty(name) || value == null)
085         return null;
086      return new BasicCsvArrayHeader(name, value);
087   }
088
089   private List<String> parsed;
090
091   /**
092    * Constructor.
093    *
094    * @param name The header name.
095    * @param value
096    *    The header value.
097    *    <br>Can be any of the following:
098    *    <ul>
099    *       <li><c>String</c> - A comma-delimited string.
100    *       <li><c>String[]</c> - A pre-parsed value.
101    *       <li>Any other array type - Converted to <c>String[]</c>.
102    *       <li>Any {@link Collection} - Converted to <c>String[]</c>.
103    *       <li>Anything else - Converted to <c>String</c>.
104    *       <li>A {@link Supplier} of anything on this list.
105    *    </ul>
106    */
107   public BasicCsvArrayHeader(String name, Object value) {
108      super(name, value);
109      if (! isSupplier(value))
110         parsed = getParsedValue();
111   }
112
113   @Override /* Header */
114   public String getValue() {
115      Object o = getRawValue();
116      if (o instanceof String)
117         return (String)o;
118      return joine(getParsedValue(), ',');
119   }
120
121   /**
122    * Returns <jk>true</jk> if this header contains the specified value.
123    *
124    * @param val The value to check for.
125    * @return <jk>true</jk> if this header contains the specified value.
126    */
127   public boolean contains(String val) {
128      List<String> vv = getParsedValue();
129      if (val != null && vv != null)
130         for (String v : vv)
131            if (isEquals(v, val))
132               return true;
133      return false;
134   }
135
136   /**
137    * Returns <jk>true</jk> if this header contains the specified value using {@link String#equalsIgnoreCase(String)}.
138    *
139    * @param val The value to check for.
140    * @return <jk>true</jk> if this header contains the specified value.
141    */
142   public boolean containsIc(String val) {
143      List<String> vv = getParsedValue();
144      if (val != null && vv != null)
145         for (String v : vv)
146            if (isEqualsIc(v, val))
147               return true;
148      return false;
149   }
150
151   /**
152    * Provides the ability to perform fluent-style assertions on this header.
153    *
154    * <h5 class='section'>Examples:</h5>
155    * <p class='bcode w800'>
156    *    <jc>// Validates the response body content is not expired.</jc>
157    *    client
158    *       .get(<jsf>URL</jsf>)
159    *       .run()
160    *       .(<js>"Expires"</js>).assertThat().isLessThan(<jk>new</jk> Date());
161    * </p>
162    *
163    * @return A new fluent assertion object.
164    * @throws AssertionError If assertion failed.
165    */
166   public FluentListAssertion<BasicCsvArrayHeader> assertList() {
167      return new FluentListAssertion<>(asList(), this);
168   }
169
170   /**
171    * Returns the contents of this header as a list of strings.
172    *
173    * @return The contents of this header as an unmodifiable list of strings, or <jk>null</jk> if the value was <jk>null</jk>.
174    */
175   public List<String> asList() {
176      List<String> l = getParsedValue();
177      return l == null ? null : Collections.unmodifiableList(l);
178   }
179
180   private List<String> getParsedValue() {
181      if (parsed != null)
182         return parsed;
183
184      Object o = getRawValue();
185      if (o == null)
186         return null;
187
188      AList<String> l = AList.of();
189      if (o instanceof Collection) {
190         for (Object o2 : (Collection<?>)o)
191            l.add(stringify(o2));
192      } else if (o.getClass().isArray()) {
193         for (int i = 0; i < Array.getLength(o); i++)
194            l.add(stringify(Array.get(o, i)));
195      } else {
196         for (String s : split(o.toString()))
197            l.add(s);
198      }
199      return l.unmodifiable();
200   }
201}