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}