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.common.internal.StringUtils.*;
016import static org.apache.juneau.internal.CollectionUtils.*;
017
018import java.util.*;
019import java.util.function.*;
020
021import org.apache.juneau.*;
022
023/**
024 * Category of headers that consist of simple comma-delimited lists of strings with q-values.
025 *
026 * <p>
027 * <h5 class='figure'>Example</h5>
028 * <p class='bcode'>
029 *    Accept-Encoding: compress;q=0.5, gzip;q=1.0
030 * </p>
031 *
032 * <h5 class='section'>See Also:</h5><ul>
033 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a>
034 *    <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
035 * </ul>
036 *
037 * @serial exclude
038 */
039public class BasicStringRangesHeader extends BasicHeader {
040
041   //-----------------------------------------------------------------------------------------------------------------
042   // Static
043   //-----------------------------------------------------------------------------------------------------------------
044
045   private static final long serialVersionUID = 1L;
046
047   /**
048    * Static creator.
049    *
050    * @param name The header name.
051    * @param value
052    *    The header value.
053    *    <br>Must be parsable by {@link StringRanges#of(String)}.
054    *    <br>Can be <jk>null</jk>.
055    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
056    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
057    */
058   public static BasicStringRangesHeader of(String name, String value) {
059      return value == null ? null : new BasicStringRangesHeader(name, value);
060   }
061
062   /**
063    * Static creator.
064    *
065    * @param name The header name.
066    * @param value
067    *    The header value.
068    *    <br>Can be <jk>null</jk>.
069    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
070    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
071    */
072   public static BasicStringRangesHeader of(String name, StringRanges value) {
073      return value == null ? null : new BasicStringRangesHeader(name, value);
074   }
075
076   /**
077    * Static creator with delayed value.
078    *
079    * <p>
080    * Header value is re-evaluated on each call to {@link #getValue()}.
081    *
082    * @param name The header name.
083    * @param value
084    *    The supplier of the header value.
085    *    <br>Can be <jk>null</jk>.
086    * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>.
087    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
088    */
089   public static BasicStringRangesHeader of(String name, Supplier<StringRanges> value) {
090      return value == null ? null : new BasicStringRangesHeader(name, value);
091   }
092
093   //-----------------------------------------------------------------------------------------------------------------
094   // Instance
095   //-----------------------------------------------------------------------------------------------------------------
096
097   private final String stringValue;
098   private final StringRanges value;
099   private final Supplier<StringRanges> supplier;
100
101   /**
102    * Constructor.
103    *
104    * @param name The header name.
105    * @param value
106    *    The header value.
107    *    <br>Must be parsable by {@link StringRanges#of(String)}.
108    *    <br>Can be <jk>null</jk>.
109    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
110    */
111   public BasicStringRangesHeader(String name, String value) {
112      super(name, value);
113      this.stringValue = value;
114      this.value = StringRanges.of(value);
115      this.supplier = null;
116   }
117
118   /**
119    * Constructor.
120    *
121    * @param name The header name.
122    * @param value
123    *    The header value.
124    *    <br>Can be <jk>null</jk>.
125    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
126    */
127   public BasicStringRangesHeader(String name, StringRanges value) {
128      super(name, stringify(value));
129      this.stringValue = null;
130      this.value = value;
131      this.supplier = null;
132   }
133
134   /**
135    * Constructor with delayed value.
136    *
137    * <p>
138    * Header value is re-evaluated on each call to {@link #getValue()}.
139    *
140    * @param name The header name.
141    * @param value
142    *    The supplier of the header value.
143    *    <br>Can be <jk>null</jk>.
144    * @throws IllegalArgumentException If name is <jk>null</jk> or empty.
145    */
146   public BasicStringRangesHeader(String name, Supplier<StringRanges> value) {
147      super(name, null);
148      this.stringValue = null;
149      this.value = null;
150      this.supplier = value;
151   }
152
153   @Override /* Header */
154   public String getValue() {
155      return stringValue != null ? stringValue : stringify(value());
156   }
157
158   /**
159    * Returns the header value as a {@link StringRanges} wrapped in an {@link Optional}.
160    *
161    * @return The header value as a {@link StringRanges} wrapped in an {@link Optional}.  Never <jk>null</jk>.
162    */
163   public Optional<StringRanges> asStringRanges() {
164      return optional(value());
165   }
166
167   /**
168    * Returns the header value as a {@link StringRanges}.
169    *
170    * @return The header value as a {@link StringRanges}.  Can be <jk>null</jk>.
171    */
172   public StringRanges toStringRanges() {
173      return value();
174   }
175
176   /**
177    * Given a list of media types, returns the best match for this string range header.
178    *
179    * <p>
180    * Note that fuzzy matching is allowed on the media types where the string range header may
181    * contain additional subtype parts.
182    * <br>For example, given identical q-values and an string range value of <js>"text/json+activity"</js>,
183    * the media type <js>"text/json"</js> will match if <js>"text/json+activity"</js> or <js>"text/activity+json"</js>
184    * isn't found.
185    * <br>The purpose for this is to allow serializers to match when artifacts such as <c>id</c> properties are
186    * present in the header.
187    *
188    * <p>
189    * See <a class="doclink" href="https://www.w3.org/TR/activitypub/#retrieving-objects">ActivityPub / Retrieving Objects</a>
190    *
191    * @param names The names to match against.
192    * @return The index into the array of the best match, or <c>-1</c> if no suitable matches could be found.
193    */
194   public int match(List<String> names) {
195      StringRanges x = value();
196      return x == null ? -1 : x.match(names);
197   }
198
199   /**
200    * Returns the {@link MediaRange} at the specified index.
201    *
202    * @param index The index position of the media range.
203    * @return The {@link MediaRange} at the specified index or <jk>null</jk> if the index is out of range.
204    */
205   public StringRange getRange(int index) {
206      StringRanges x = value();
207      return x == null ? null : x.getRange(index);
208   }
209
210   /**
211    * Return the value if present, otherwise return <c>other</c>.
212    *
213    * <p>
214    * This is a shortened form for calling <c>asArray().orElse(<jv>other</jv>)</c>.
215    *
216    * @param other The value to be returned if there is no value present, can be <jk>null</jk>.
217    * @return The value, if present, otherwise <c>other</c>.
218    */
219   public StringRanges orElse(StringRanges other) {
220      StringRanges x = value();
221      return x != null ? x : other;
222   }
223
224   private StringRanges value() {
225      if (supplier != null)
226         return supplier.get();
227      return value;
228   }
229}