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