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 (the
006 * "License"); you may not use this file except in compliance with the
007 * 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.Shorts.*;
020
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.commons.http.*;
025
026
027/**
028 * Base for comma-separated tokens with optional q-values.
029 *
030 * @since 9.2.1
031 */
032@SuppressWarnings({
033   "java:S2160", // equals/hashCode are inherited from HttpHeaderBean (name and wire value); lazySupplier/lazyMode only control how the value is produced
034   "java:S9149", // Public Juneau DSL factory; hiding parent of()/create() is intentional.
035   "unchecked" // Supplier<?> is cast to Supplier<String>/Supplier<StringRanges> according to the lazyMode discriminator chosen by the factory methods
036})
037public class HttpStringRangesHeader extends HttpHeaderBean {
038
039   public static final int LAZY_WIRE_STRING = 0;
040   public static final int LAZY_STRING_RANGES = 1;
041
042   private final StringRanges cachedForStringOrDirect;
043   private final Supplier<?> lazySupplier;
044   private final int lazyMode;
045
046   /**
047    * Creates an {@link HttpStringRangesHeader} by parsing the given wire-format value.
048    *
049    * @param name Header name. Must not be {@code null}.
050    * @param wireValue Wire value (e.g. {@code "gzip;q=0.5, identity"}). May be {@code null}.
051    * @return A new instance. Never {@code null}.
052    */
053   public static HttpStringRangesHeader of(String name, String wireValue) {
054      return new HttpStringRangesHeader(name, wireValue);
055   }
056
057   /**
058    * Creates an {@link HttpStringRangesHeader} with the given typed value.
059    *
060    * @param name Header name. Must not be {@code null}.
061    * @param typedValue The string-ranges value. May be {@code null}.
062    * @return A new instance. Never {@code null}.
063    */
064   public static HttpStringRangesHeader of(String name, StringRanges typedValue) {
065      return new HttpStringRangesHeader(name, typedValue);
066   }
067
068   protected HttpStringRangesHeader(String name, String value) {
069      super(name, value);
070      this.cachedForStringOrDirect = value == null ? null : StringRanges.of(value);
071      this.lazySupplier = null;
072      this.lazyMode = -1;
073   }
074
075   protected HttpStringRangesHeader(String name, StringRanges value) {
076      super(name, value == null ? null : value.toString());
077      this.cachedForStringOrDirect = value;
078      this.lazySupplier = null;
079      this.lazyMode = -1;
080   }
081
082   protected HttpStringRangesHeader(String name, Supplier<?> supplier, int lazyMode) {
083      super(name, lazyMode == LAZY_WIRE_STRING
084         ? ((Supplier<String>) supplier)::get
085         : () -> {
086            var m = ((Supplier<StringRanges>) supplier).get();
087            if (m == null)
088               return null;
089            return m.toString();
090         });
091      this.cachedForStringOrDirect = null;
092      this.lazySupplier = supplier;
093      this.lazyMode = lazyMode;
094   }
095
096   public Optional<StringRanges> asStringRanges() {
097      return o(toStringRanges());
098   }
099
100   public StringRange getRange(int index) {
101      var x = toStringRanges();
102      return x == null ? null : x.getRange(index);
103   }
104
105   public int match(List<String> names) {
106      var x = toStringRanges();
107      return x == null ? -1 : x.match(names);
108   }
109
110   public StringRanges orElse(StringRanges other) {
111      var x = toStringRanges();
112      return nn(x) ? x : other;
113   }
114
115   public StringRanges toStringRanges() {
116      if (lazyMode == LAZY_STRING_RANGES)
117         return ((Supplier<StringRanges>) lazySupplier).get();
118      if (cachedForStringOrDirect != null)
119         return cachedForStringOrDirect;
120      var v = super.getValue();
121      return v == null ? null : StringRanges.of(v);
122   }
123}