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 java.util.*; 020import java.util.function.*; 021 022import org.apache.juneau.*; 023import org.apache.juneau.common.utils.*; 024 025/** 026 * Category of headers that consist of multiple parameterized string values. 027 * 028 * <p> 029 * <h5 class='figure'>Example</h5> 030 * <p class='bcode'> 031 * Accept: application/json;q=0.9,text/xml;q=0.1 032 * </p> 033 * 034 * <h5 class='section'>See Also:</h5><ul> 035 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a> 036 * <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a> 037 * </ul> 038 * 039 * @serial exclude 040 */ 041public class BasicMediaRangesHeader extends BasicStringHeader { 042 043 //----------------------------------------------------------------------------------------------------------------- 044 // Static 045 //----------------------------------------------------------------------------------------------------------------- 046 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 MediaRanges#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 BasicMediaRangesHeader of(String name, String 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>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 BasicMediaRangesHeader of(String name, MediaRanges value) { 075 return value == null ? null : new BasicMediaRangesHeader(name, value); 076 } 077 078 //----------------------------------------------------------------------------------------------------------------- 079 // Instance 080 //----------------------------------------------------------------------------------------------------------------- 081 082 private final String stringValue; 083 private final MediaRanges value; 084 private final Supplier<MediaRanges> supplier; 085 086 /** 087 * Constructor. 088 * 089 * @param name The header name. 090 * @param value 091 * The header value. 092 * <br>Must be parsable by {@link MediaRanges#of(String)}. 093 * <br>Can be <jk>null</jk>. 094 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 095 */ 096 public BasicMediaRangesHeader(String name, String value) { 097 super(name, value); 098 this.stringValue = value; 099 this.value = parse(value); 100 this.supplier = null; 101 } 102 103 /** 104 * Constructor. 105 * 106 * @param name The header name. 107 * @param value 108 * The header value. 109 * <br>Can be <jk>null</jk>. 110 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 111 */ 112 public BasicMediaRangesHeader(String name, MediaRanges value) { 113 super(name, Utils.s(value)); 114 this.stringValue = null; 115 this.value = value; 116 this.supplier = null; 117 } 118 119 /** 120 * Constructor with delayed value. 121 * 122 * <p> 123 * Header value is re-evaluated on each call to {@link #getValue()}. 124 * 125 * @param name The header name. 126 * @param value 127 * The supplier of the header value. 128 * <br>Can be <jk>null</jk>. 129 * @throws IllegalArgumentException If name is <jk>null</jk> or empty. 130 */ 131 public BasicMediaRangesHeader(String name, Supplier<MediaRanges> value) { 132 super(name, (String)null); 133 this.stringValue = null; 134 this.value = null; 135 this.supplier = value; 136 } 137 138 /** 139 * Returns the header value as a {@link MediaRanges} wrapped in an {@link Optional}. 140 * 141 * @return The header value as a {@link MediaRanges} wrapped in an {@link Optional}. Never <jk>null</jk>. 142 */ 143 public Optional<MediaRanges> asMediaRanges() { 144 return Utils.opt(value()); 145 } 146 147 /** 148 * Returns the header value as a {@link MediaRanges}. 149 * 150 * @return The header value as a {@link MediaRanges}. Can be <jk>null</jk>. 151 */ 152 public MediaRanges toMediaRanges() { 153 return value(); 154 } 155 156 /** 157 * Given a list of media types, returns the best match for this <c>Accept</c> header. 158 * 159 * <p> 160 * Note that fuzzy matching is allowed on the media types where the <c>Accept</c> header may 161 * contain additional subtype parts. 162 * <br>For example, given identical q-values and an <c>Accept</c> value of <js>"text/json+activity"</js>, 163 * the media type <js>"text/json"</js> will match if <js>"text/json+activity"</js> or <js>"text/activity+json"</js> 164 * isn't found. 165 * <br>The purpose for this is to allow serializers to match when artifacts such as <c>id</c> properties are 166 * present in the header. 167 * 168 * <p> 169 * See <a class="doclink" href="https://www.w3.org/TR/activitypub/#retrieving-objects">ActivityPub / Retrieving Objects</a> 170 * 171 * @param mediaTypes The media types to match against. 172 * @return The index into the array of the best match, or <c>-1</c> if no suitable matches could be found. 173 */ 174 public int match(List<? extends MediaType> mediaTypes) { 175 MediaRanges x = value(); 176 return x == null ? -1 : x.match(mediaTypes); 177 } 178 179 /** 180 * Returns the {@link MediaRange} at the specified index. 181 * 182 * @param index The index position of the media range. 183 * @return The {@link MediaRange} at the specified index or <jk>null</jk> if the index is out of range. 184 */ 185 public MediaRange getRange(int index) { 186 MediaRanges x = value(); 187 return x == null ? null : x.getRange(index); 188 } 189 190 /** 191 * Convenience method for searching through all of the subtypes of all the media ranges in this header for the 192 * presence of a subtype fragment. 193 * 194 * <p> 195 * For example, given the header <js>"text/json+activity"</js>, calling 196 * <code>hasSubtypePart(<js>"activity"</js>)</code> returns <jk>true</jk>. 197 * 198 * @param part The media type subtype fragment. 199 * @return <jk>true</jk> if subtype fragment exists. 200 */ 201 public boolean hasSubtypePart(String part) { 202 MediaRanges x = value(); 203 return x == null ? false : x.hasSubtypePart(part); 204 } 205 206 @Override /* Header */ 207 public String getValue() { 208 return stringValue != null ? stringValue : Utils.s(value()); 209 } 210 211 /** 212 * Return the value if present, otherwise return <c>other</c>. 213 * 214 * <p> 215 * This is a shortened form for calling <c>asMediaRanges().orElse(<jv>other</jv>)</c>. 216 * 217 * @param other The value to be returned if there is no value present, can be <jk>null</jk>. 218 * @return The value, if present, otherwise <c>other</c>. 219 */ 220 public MediaRanges orElse(MediaRanges other) { 221 MediaRanges x = value(); 222 return x != null ? x : other; 223 } 224 225 private MediaRanges parse(String value) { 226 return value == null ? null : MediaRanges.of(value); 227 } 228 229 private MediaRanges value() { 230 if (supplier != null) 231 return supplier.get(); 232 return value; 233 } 234}