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 multiple parameterized string values. 029 * 030 * <p> 031 * <h5 class='figure'>Example</h5> 032 * <p class='bcode'> 033 * Accept: application/json;q=0.9,text/xml;q=0.1 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 "java:S9149" // Public Juneau DSL factory; hiding parent of()/create() is intentional. 046}) 047public class BasicMediaRangesHeader extends BasicStringHeader { 048 private static final long serialVersionUID = 1L; 049 050 /** 051 * Static creator. 052 * 053 * @param name The header name. 054 * @param value 055 * The header value. 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 BasicMediaRangesHeader of(String name, MediaRanges value) { 061 return value == null ? null : new BasicMediaRangesHeader(name, value); 062 } 063 064 /** 065 * Static creator. 066 * 067 * @param name The header name. 068 * @param value 069 * The header value. 070 * <br>Must be parsable by {@link MediaRanges#of(String)}. 071 * <br>Can be <jk>null</jk>. 072 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 073 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 074 */ 075 public static BasicMediaRangesHeader of(String name, String value) { 076 return value == null ? null : new BasicMediaRangesHeader(name, value); 077 } 078 079 private final String stringValue; 080 private final transient MediaRanges value; 081 private final transient Supplier<MediaRanges> supplier; 082 083 /** 084 * Constructor. 085 * 086 * @param name The header name. 087 * @param value 088 * The header value. 089 * <br>Can be <jk>null</jk> to create a header with no value. 090 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 091 */ 092 public BasicMediaRangesHeader(String name, MediaRanges value) { 093 super(name, s(value)); 094 this.stringValue = null; 095 this.value = value; 096 this.supplier = null; 097 } 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 MediaRanges#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 BasicMediaRangesHeader(String name, String value) { 110 super(name, value); 111 stringValue = value; 112 this.value = parse(value); 113 this.supplier = null; 114 } 115 116 /** 117 * Constructor with delayed value. 118 * 119 * <p> 120 * Header value is re-evaluated on each call to {@link #getValue()}. 121 * 122 * @param name The header name. 123 * @param value 124 * The supplier of the header value. 125 * <br>Can be <jk>null</jk> to create a header with no value. 126 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 127 */ 128 public BasicMediaRangesHeader(String name, Supplier<MediaRanges> value) { 129 super(name, (String)null); 130 this.stringValue = null; 131 this.value = null; 132 supplier = value; 133 } 134 135 /** 136 * Returns the header value as a {@link MediaRanges} wrapped in an {@link Optional}. 137 * 138 * @return The header value as a {@link MediaRanges} wrapped in an {@link Optional}. Never <jk>null</jk>. 139 */ 140 public Optional<MediaRanges> asMediaRanges() { 141 return o(value()); 142 } 143 144 /** 145 * Returns the {@link MediaRange} at the specified index. 146 * 147 * @param index The index position of the media range. 148 * @return The {@link MediaRange} at the specified index or <jk>null</jk> if the index is out of range. 149 */ 150 public MediaRange getRange(int index) { 151 MediaRanges x = value(); 152 return x == null ? null : x.getRange(index); 153 } 154 155 @Override /* Overridden from Header */ 156 public String getValue() { return nn(stringValue) ? stringValue : s(value()); } 157 158 /** 159 * Convenience method for searching through all of the subtypes of all the media ranges in this header for the 160 * presence of a subtype fragment. 161 * 162 * <p> 163 * For example, given the header <js>"text/json+activity"</js>, calling 164 * <code>hasSubtypePart(<js>"activity"</js>)</code> returns <jk>true</jk>. 165 * 166 * @param part The media type subtype fragment. 167 * @return <jk>true</jk> if subtype fragment exists. 168 */ 169 public boolean hasSubtypePart(String part) { 170 MediaRanges x = value(); 171 return x != null && x.hasSubtypePart(part); 172 } 173 174 /** 175 * Given a list of media types, returns the best match for this <c>Accept</c> header. 176 * 177 * <p> 178 * Note that fuzzy matching is allowed on the media types where the <c>Accept</c> header may 179 * contain additional subtype parts. 180 * <br>For example, given identical q-values and an <c>Accept</c> 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 mediaTypes The media types 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<? extends MediaType> mediaTypes) { 193 MediaRanges x = value(); 194 return x == null ? -1 : x.match(mediaTypes); 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>asMediaRanges().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 MediaRanges orElse(MediaRanges other) { 207 MediaRanges x = value(); 208 return nn(x) ? x : other; 209 } 210 211 /** 212 * Returns the header value as a {@link MediaRanges}. 213 * 214 * @return The header value as a {@link MediaRanges}, or <jk>null</jk> if the header has no value. 215 */ 216 public MediaRanges toMediaRanges() { 217 return value(); 218 } 219 220 private static MediaRanges parse(String value) { 221 return value == null ? null : MediaRanges.of(value); 222 } 223 224 @SuppressWarnings({ 225 "java:S2177" // Intentional: subclass provides its own private value() with a different return type (MediaRanges vs String) 226 }) 227 private MediaRanges value() { 228 if (nn(supplier)) 229 return supplier.get(); 230 return value; 231 } 232}