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