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.commons.http; 018 019import static org.apache.juneau.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.util.*; 023import java.util.function.*; 024 025/** 026 * Describes a single type used in content negotiation between an HTTP client and server, as described in 027 * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification). 028 * 029 * <h5 class='section'>See Also:</h5><ul> 030 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a> 031 * <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a> 032 * </ul> 033 */ 034public class MediaRange extends MediaType { 035 036 private final NameValuePair[] extensions; 037 private final Float qValue; 038 private final String string; 039 040 /** 041 * Constructor. 042 * 043 * @param e The parsed media range element. 044 */ 045 public MediaRange(HeaderElement e) { 046 super(e); 047 048 Float qValue2 = 1f; 049 050 // The media type consists of everything up to the q parameter. 051 // The q parameter and stuff after is part of the range. 052 List<NameValuePair> extensions2 = list(); 053 boolean foundQ = false; 054 for (var p : e.getParameters()) { 055 if (eq(p.getName(), "q")) { 056 qValue2 = Float.parseFloat(p.getValue()); 057 foundQ = true; 058 } else if (foundQ) { 059 extensions2.add(new BasicNameValuePair(p.getName(), p.getValue())); 060 } 061 } 062 063 this.qValue = qValue2; 064 this.extensions = extensions2.toArray(NameValuePair.EMPTY_ARRAY); 065 066 var sb = new StringBuilder().append(super.toString()); 067 068 // '1' is equivalent to specifying no qValue. If there's no extensions, then we won't include a qValue. 069 if (qValue2.floatValue() == 1.0) { 070 if (this.extensions.length > 0) { 071 sb.append(";q=").append(qValue); 072 Arrays.stream(extensions).forEach(x -> sb.append(';').append(x.getName()).append('=').append(x.getValue())); 073 } 074 } else { 075 sb.append(";q=").append(qValue); 076 Arrays.stream(extensions).forEach(x -> sb.append(';').append(x.getName()).append('=').append(x.getValue())); 077 } 078 string = sb.toString(); 079 } 080 081 /** 082 * Performs an action on the optional set of custom extensions defined for this type. 083 * 084 * <p> 085 * Values are lowercase and never <jk>null</jk>. 086 * 087 * @param action The action to perform. 088 * @return This object. 089 */ 090 public MediaRange forEachExtension(Consumer<NameValuePair> action) { 091 for (var r : extensions) 092 action.accept(r); 093 return this; 094 } 095 096 @Override /* Overridden from MediaType */ 097 public MediaRange forEachParameter(Consumer<NameValuePair> action) { 098 super.forEachParameter(action); 099 return this; 100 } 101 102 /** 103 * Returns the optional set of custom extensions defined for this type. 104 * 105 * <p> 106 * Values are lowercase and never <jk>null</jk>. 107 * 108 * @return The optional list of extensions, never <jk>null</jk>. 109 */ 110 public List<NameValuePair> getExtensions() { return u(l(extensions)); } 111 112 /** 113 * Returns the <js>'q'</js> (quality) value for this type, as described in Section 3.9 of RFC2616. 114 * 115 * <p> 116 * The quality value is a float between <c>0.0</c> (unacceptable) and <c>1.0</c> (most acceptable). 117 * 118 * <p> 119 * If 'q' value doesn't make sense for the context (e.g. this range was extracted from a <js>"content-*"</js> 120 * header, as opposed to <js>"accept-*"</js> header, its value will always be <js>"1"</js>. 121 * 122 * @return The 'q' value for this type, never <jk>null</jk>. 123 */ 124 public Float getQValue() { return qValue; } 125 126 @Override /* Overridden from Object */ 127 public String toString() { 128 return string; 129 } 130 131 @Override /* Overridden from Object */ 132 public boolean equals(Object o) { 133 return o instanceof MediaRange o2 && eq(this, o2, (x, y) -> eq(x.string, y.string)); 134 } 135 136 @Override /* Overridden from Object */ 137 public int hashCode() { 138 return Objects.hashCode(string); 139 } 140}