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