001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.http.header; 014 015import static org.apache.juneau.common.internal.StringUtils.*; 016import static org.apache.juneau.internal.CollectionUtils.*; 017 018import java.util.*; 019import java.util.function.*; 020 021import org.apache.juneau.*; 022 023/** 024 * Category of headers that consist of multiple parameterized string values. 025 * 026 * <p> 027 * <h5 class='figure'>Example</h5> 028 * <p class='bcode'> 029 * Accept: application/json;q=0.9,text/xml;q=0.1 030 * </p> 031 * 032 * <h5 class='section'>See Also:</h5><ul> 033 * <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a> 034 * <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a> 035 * </ul> 036 * 037 * @serial exclude 038 */ 039public class BasicMediaRangesHeader extends BasicStringHeader { 040 041 //----------------------------------------------------------------------------------------------------------------- 042 // Static 043 //----------------------------------------------------------------------------------------------------------------- 044 045 private static final long serialVersionUID = 1L; 046 047 /** 048 * Static creator. 049 * 050 * @param name The header name. 051 * @param value 052 * The header value. 053 * <br>Must be parsable by {@link MediaRanges#of(String)}. 054 * <br>Can be <jk>null</jk>. 055 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 056 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 057 */ 058 public static BasicMediaRangesHeader of(String name, String value) { 059 return value == null ? null : new BasicMediaRangesHeader(name, value); 060 } 061 062 /** 063 * Static creator. 064 * 065 * @param name The header name. 066 * @param value 067 * The header value. 068 * <br>Can be <jk>null</jk>. 069 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 070 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 071 */ 072 public static BasicMediaRangesHeader of(String name, MediaRanges value) { 073 return value == null ? null : new BasicMediaRangesHeader(name, value); 074 } 075 076 //----------------------------------------------------------------------------------------------------------------- 077 // Instance 078 //----------------------------------------------------------------------------------------------------------------- 079 080 private final String stringValue; 081 private final MediaRanges value; 082 private final Supplier<MediaRanges> supplier; 083 084 /** 085 * Constructor. 086 * 087 * @param name The header name. 088 * @param value 089 * The header value. 090 * <br>Must be parsable by {@link MediaRanges#of(String)}. 091 * <br>Can be <jk>null</jk>. 092 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 093 */ 094 public BasicMediaRangesHeader(String name, String value) { 095 super(name, value); 096 this.stringValue = value; 097 this.value = parse(value); 098 this.supplier = null; 099 } 100 101 /** 102 * Constructor. 103 * 104 * @param name The header name. 105 * @param value 106 * The header value. 107 * <br>Can be <jk>null</jk>. 108 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 109 */ 110 public BasicMediaRangesHeader(String name, MediaRanges value) { 111 super(name, stringify(value)); 112 this.stringValue = null; 113 this.value = value; 114 this.supplier = null; 115 } 116 117 /** 118 * Constructor with delayed value. 119 * 120 * <p> 121 * Header value is re-evaluated on each call to {@link #getValue()}. 122 * 123 * @param name The header name. 124 * @param value 125 * The supplier of the header value. 126 * <br>Can be <jk>null</jk>. 127 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 128 */ 129 public BasicMediaRangesHeader(String name, Supplier<MediaRanges> value) { 130 super(name, (String)null); 131 this.stringValue = null; 132 this.value = null; 133 this.supplier = value; 134 } 135 136 /** 137 * Returns the header value as a {@link MediaRanges} wrapped in an {@link Optional}. 138 * 139 * @return The header value as a {@link MediaRanges} wrapped in an {@link Optional}. Never <jk>null</jk>. 140 */ 141 public Optional<MediaRanges> asMediaRanges() { 142 return optional(value()); 143 } 144 145 /** 146 * Returns the header value as a {@link MediaRanges}. 147 * 148 * @return The header value as a {@link MediaRanges}. Can be <jk>null</jk>. 149 */ 150 public MediaRanges toMediaRanges() { 151 return value(); 152 } 153 154 /** 155 * Given a list of media types, returns the best match for this <c>Accept</c> header. 156 * 157 * <p> 158 * Note that fuzzy matching is allowed on the media types where the <c>Accept</c> header may 159 * contain additional subtype parts. 160 * <br>For example, given identical q-values and an <c>Accept</c> value of <js>"text/json+activity"</js>, 161 * the media type <js>"text/json"</js> will match if <js>"text/json+activity"</js> or <js>"text/activity+json"</js> 162 * isn't found. 163 * <br>The purpose for this is to allow serializers to match when artifacts such as <c>id</c> properties are 164 * present in the header. 165 * 166 * <p> 167 * See <a class="doclink" href="https://www.w3.org/TR/activitypub/#retrieving-objects">ActivityPub / Retrieving Objects</a> 168 * 169 * @param mediaTypes The media types to match against. 170 * @return The index into the array of the best match, or <c>-1</c> if no suitable matches could be found. 171 */ 172 public int match(List<? extends MediaType> mediaTypes) { 173 MediaRanges x = value(); 174 return x == null ? -1 : x.match(mediaTypes); 175 } 176 177 /** 178 * Returns the {@link MediaRange} at the specified index. 179 * 180 * @param index The index position of the media range. 181 * @return The {@link MediaRange} at the specified index or <jk>null</jk> if the index is out of range. 182 */ 183 public MediaRange getRange(int index) { 184 MediaRanges x = value(); 185 return x == null ? null : x.getRange(index); 186 } 187 188 /** 189 * Convenience method for searching through all of the subtypes of all the media ranges in this header for the 190 * presence of a subtype fragment. 191 * 192 * <p> 193 * For example, given the header <js>"text/json+activity"</js>, calling 194 * <code>hasSubtypePart(<js>"activity"</js>)</code> returns <jk>true</jk>. 195 * 196 * @param part The media type subtype fragment. 197 * @return <jk>true</jk> if subtype fragment exists. 198 */ 199 public boolean hasSubtypePart(String part) { 200 MediaRanges x = value(); 201 return x == null ? false : x.hasSubtypePart(part); 202 } 203 204 @Override /* Header */ 205 public String getValue() { 206 return stringValue != null ? stringValue : stringify(value()); 207 } 208 209 /** 210 * Return the value if present, otherwise return <c>other</c>. 211 * 212 * <p> 213 * This is a shortened form for calling <c>asMediaRanges().orElse(<jv>other</jv>)</c>. 214 * 215 * @param other The value to be returned if there is no value present, can be <jk>null</jk>. 216 * @return The value, if present, otherwise <c>other</c>. 217 */ 218 public MediaRanges orElse(MediaRanges other) { 219 MediaRanges x = value(); 220 return x != null ? x : other; 221 } 222 223 private MediaRanges parse(String value) { 224 return value == null ? null : MediaRanges.of(value); 225 } 226 227 private MediaRanges value() { 228 if (supplier != null) 229 return supplier.get(); 230 return value; 231 } 232}