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