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;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.http.*;
025import org.apache.http.message.*;
026
027/**
028 * Describes a single type used in content negotiation between an HTTP client and server, as described in
029 * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
030 *
031 * <h5 class='section'>See Also:</h5><ul>
032 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommonBasics">juneau-rest-common Basics</a>
033 *    <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
034 * </ul>
035 */
036public class MediaRange extends MediaType {
037
038   private final NameValuePair[] extensions;
039   private final Float qValue;
040   private final String string;
041
042   /**
043    * Constructor.
044    *
045    * @param e The parsed media range element.
046    */
047   public MediaRange(HeaderElement e) {
048      super(e);
049
050      Float qValue = 1f;
051
052      // The media type consists of everything up to the q parameter.
053      // The q parameter and stuff after is part of the range.
054      List<NameValuePair> extensions = list();
055      boolean foundQ = false;
056      for (var p : e.getParameters()) {
057         if (p.getName().equals("q")) {
058            qValue = Float.parseFloat(p.getValue());
059            foundQ = true;
060         } else if (foundQ) {
061            extensions.add(new BasicNameValuePair(p.getName(), p.getValue()));
062         }
063      }
064
065      this.qValue = qValue;
066      this.extensions = extensions.toArray(new NameValuePair[extensions.size()]);
067
068      var sb = new StringBuffer().append(super.toString());
069
070      // '1' is equivalent to specifying no qValue. If there's no extensions, then we won't include a qValue.
071      if (qValue.floatValue() == 1.0) {
072         if (this.extensions.length > 0) {
073            sb.append(";q=").append(qValue);
074            extensions.forEach(x -> sb.append(';').append(x.getName()).append('=').append(x.getValue()));
075         }
076      } else {
077         sb.append(";q=").append(qValue);
078         extensions.forEach(x -> sb.append(';').append(x.getName()).append('=').append(x.getValue()));
079      }
080      string = sb.toString();
081   }
082
083   /**
084    * Performs an action on the optional set of custom extensions defined for this type.
085    *
086    * <p>
087    * Values are lowercase and never <jk>null</jk>.
088    *
089    * @param action The action to perform.
090    * @return This object.
091    */
092   public MediaRange forEachExtension(Consumer<NameValuePair> action) {
093      for (var r : extensions)
094         action.accept(r);
095      return this;
096   }
097
098   @Override /* Overridden from MediaType */
099   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}