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 (the 006 * "License"); you may not use this file except in compliance with the 007 * 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.Shorts.*; 020 021import java.util.*; 022import java.util.function.*; 023 024import org.apache.juneau.commons.http.*; 025 026 027/** 028 * Base type for headers whose wire format is an <c>Accept</c>-style list of media ranges. 029 * 030 * <h5 class='section'>See Also:</h5><ul> 031 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NextGenRestClient">Next-Generation REST Client</a> 032 * </ul> 033 * 034 * @since 9.2.1 035 */ 036@SuppressWarnings({ 037 "java:S2160", // equals() on HttpHeaderBean uses name + getValue(); typed state is reflected in getValue() 038 "unchecked" // Supplier<?> branches cast to typed suppliers after lazy-mode check 039}) 040public class HttpMediaRangesHeader extends HttpHeaderBean { 041 042 /** Lazy supplier provides the wire string. */ 043 public static final int LAZY_WIRE_STRING = 0; 044 045 /** Lazy supplier provides {@link MediaRanges} (re-evaluated on each access). */ 046 public static final int LAZY_MEDIA_RANGES = 1; 047 048 private final MediaRanges cachedForStringOrDirect; 049 private final Supplier<?> lazySupplier; 050 private final int lazyMode; 051 052 /** 053 * @param name Header name. Must not be {@code null}. 054 * @param value Wire value. May be {@code null}. 055 */ 056 protected HttpMediaRangesHeader(String name, String value) { 057 super(name, value); 058 this.cachedForStringOrDirect = value == null ? null : MediaRanges.of(value); 059 this.lazySupplier = null; 060 this.lazyMode = -1; 061 } 062 063 /** 064 * @param name Header name. Must not be {@code null}. 065 * @param value Parsed media ranges. May be {@code null}. 066 */ 067 protected HttpMediaRangesHeader(String name, MediaRanges value) { 068 super(name, value == null ? null : value.toString()); 069 this.cachedForStringOrDirect = value; 070 this.lazySupplier = null; 071 this.lazyMode = -1; 072 } 073 074 /** 075 * @param name Header name. Must not be {@code null}. 076 * @param supplier Lazy supplier; mode {@link #LAZY_WIRE_STRING} or {@link #LAZY_MEDIA_RANGES}. Must not be {@code null}. 077 * @param lazyMode Discriminator for erasure-safe lazy construction. 078 */ 079 protected HttpMediaRangesHeader(String name, Supplier<?> supplier, int lazyMode) { 080 super(name, lazyMode == LAZY_WIRE_STRING 081 ? ((Supplier<String>) supplier)::get 082 : () -> { 083 var m = ((Supplier<MediaRanges>) supplier).get(); 084 if (m == null) 085 return null; 086 return m.toString(); 087 }); 088 this.cachedForStringOrDirect = null; 089 this.lazySupplier = supplier; 090 this.lazyMode = lazyMode; 091 } 092 093 /** 094 * @return Parsed {@link MediaRanges}, or an empty {@link Optional} if the wire value is absent. 095 */ 096 public Optional<MediaRanges> asMediaRanges() { 097 return o(toMediaRanges()); 098 } 099 100 /** 101 * @param index Range index. 102 * @return The {@link MediaRange} at {@code index}, or {@code null} if out of range. 103 */ 104 public MediaRange getRange(int index) { 105 var x = toMediaRanges(); 106 return x == null ? null : x.getRange(index); 107 } 108 109 /** 110 * @param part Subtype fragment to find (e.g. {@code "activity"} for {@code text/json+activity}). 111 * @return {@code true} if any range's subtype contains the fragment. 112 */ 113 public boolean hasSubtypePart(String part) { 114 var x = toMediaRanges(); 115 return x != null && x.hasSubtypePart(part); 116 } 117 118 /** 119 * @param mediaTypes Candidate types, in preference order. 120 * @return Index of the best matching type, or {@code -1}. 121 */ 122 public int match(List<? extends MediaType> mediaTypes) { 123 var x = toMediaRanges(); 124 return x == null ? -1 : x.match(mediaTypes); 125 } 126 127 /** 128 * @param other Fallback when no parsed value is present. 129 * @return Parsed ranges or {@code other}. 130 */ 131 public MediaRanges orElse(MediaRanges other) { 132 var x = toMediaRanges(); 133 return nn(x) ? x : other; 134 } 135 136 /** 137 * @return Parsed {@link MediaRanges}, or {@code null} if the wire value is absent. 138 */ 139 public MediaRanges toMediaRanges() { 140 if (lazyMode == LAZY_MEDIA_RANGES) 141 return ((Supplier<MediaRanges>) lazySupplier).get(); 142 if (cachedForStringOrDirect != null) 143 return cachedForStringOrDirect; 144 var v = super.getValue(); 145 return v == null ? null : MediaRanges.of(v); 146 } 147}