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