1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau;
18
19 import static org.apache.juneau.commons.utils.CollectionUtils.*;
20
21 import java.util.*;
22 import java.util.function.*;
23
24 import org.apache.http.*;
25 import org.apache.http.message.*;
26
27 /**
28 * Describes a single type used in content negotiation between an HTTP client and server, as described in
29 * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
30 *
31 * <h5 class='section'>See Also:</h5><ul>
32 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a>
33 * <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
34 * </ul>
35 */
36 public class MediaRange extends MediaType {
37
38 private final NameValuePair[] extensions;
39 private final Float qValue;
40 private final String string;
41
42 /**
43 * Constructor.
44 *
45 * @param e The parsed media range element.
46 */
47 public MediaRange(HeaderElement e) {
48 super(e);
49
50 Float qValue = 1f;
51
52 // The media type consists of everything up to the q parameter.
53 // The q parameter and stuff after is part of the range.
54 List<NameValuePair> extensions = list();
55 boolean foundQ = false;
56 for (var p : e.getParameters()) {
57 if (p.getName().equals("q")) {
58 qValue = Float.parseFloat(p.getValue());
59 foundQ = true;
60 } else if (foundQ) {
61 extensions.add(new BasicNameValuePair(p.getName(), p.getValue()));
62 }
63 }
64
65 this.qValue = qValue;
66 this.extensions = extensions.toArray(new NameValuePair[extensions.size()]);
67
68 var sb = new StringBuffer().append(super.toString());
69
70 // '1' is equivalent to specifying no qValue. If there's no extensions, then we won't include a qValue.
71 if (qValue.floatValue() == 1.0) {
72 if (this.extensions.length > 0) {
73 sb.append(";q=").append(qValue);
74 extensions.forEach(x -> sb.append(';').append(x.getName()).append('=').append(x.getValue()));
75 }
76 } else {
77 sb.append(";q=").append(qValue);
78 extensions.forEach(x -> sb.append(';').append(x.getName()).append('=').append(x.getValue()));
79 }
80 string = sb.toString();
81 }
82
83 /**
84 * Performs an action on the optional set of custom extensions defined for this type.
85 *
86 * <p>
87 * Values are lowercase and never <jk>null</jk>.
88 *
89 * @param action The action to perform.
90 * @return This object.
91 */
92 public MediaRange forEachExtension(Consumer<NameValuePair> action) {
93 for (var r : extensions)
94 action.accept(r);
95 return this;
96 }
97
98 @Override /* Overridden from MediaType */
99 public MediaRange forEachParameter(Consumer<NameValuePair> action) {
100 super.forEachParameter(action);
101 return this;
102 }
103
104 /**
105 * Returns the optional set of custom extensions defined for this type.
106 *
107 * <p>
108 * Values are lowercase and never <jk>null</jk>.
109 *
110 * @return The optional list of extensions, never <jk>null</jk>.
111 */
112 public List<NameValuePair> getExtensions() { return u(l(extensions)); }
113
114 /**
115 * Returns the <js>'q'</js> (quality) value for this type, as described in Section 3.9 of RFC2616.
116 *
117 * <p>
118 * The quality value is a float between <c>0.0</c> (unacceptable) and <c>1.0</c> (most acceptable).
119 *
120 * <p>
121 * If 'q' value doesn't make sense for the context (e.g. this range was extracted from a <js>"content-*"</js>
122 * header, as opposed to <js>"accept-*"</js> header, its value will always be <js>"1"</js>.
123 *
124 * @return The 'q' value for this type, never <jk>null</jk>.
125 */
126 public Float getQValue() { return qValue; }
127
128 @Override /* Overridden from Object */
129 public String toString() {
130 return string;
131 }
132 }