Coverage Report - org.apache.any23.servlet.conneg.MediaRangeSpec
 
Classes in this File Line Coverage Branch Coverage Complexity
MediaRangeSpec
0%
0/103
0%
0/52
3
 
 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  
 
 18  
 package org.apache.any23.servlet.conneg;
 19  
 
 20  
 import java.util.ArrayList;
 21  
 import java.util.Collections;
 22  
 import java.util.Iterator;
 23  
 import java.util.List;
 24  
 import java.util.regex.Matcher;
 25  
 import java.util.regex.Pattern;
 26  
 
 27  
 /**
 28  
  * This class implements the <i>HTTP header media-range specification</i>.
 29  
  * <br/>
 30  
  * See <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616 section 14.1</a>. 
 31  
  */
 32  
 public class MediaRangeSpec {
 33  
 
 34  
     private static final Pattern tokenPattern;
 35  
 
 36  
     private static final Pattern parameterPattern;
 37  
 
 38  
 
 39  
     private static final Pattern mediaRangePattern;
 40  
 
 41  
     private static final Pattern qValuePattern;
 42  
 
 43  
     private final String type;
 44  
 
 45  
     private final String subtype;
 46  
 
 47  
     private final List<String> parameterNames;
 48  
 
 49  
     private final List<String> parameterValues;
 50  
 
 51  
     private final String mediaType;
 52  
     
 53  
     private final double quality;
 54  
     
 55  
     static {
 56  
 
 57  
         // See RFC 2616, section 2.2
 58  0
         String token = "[\\x20-\\x7E&&[^()<>@,;:\\\"/\\[\\]?={} ]]+";
 59  0
         String quotedString = "\"((?:[\\x20-\\x7E\\n\\r\\t&&[^\"\\\\]]|\\\\[\\x00-\\x7F])*)\"";
 60  
 
 61  
         // See RFC 2616, section 3.6
 62  0
         String parameter = ";\\s*(?!q\\s*=)(" + token + ")=(?:(" + token + ")|" + quotedString + ")";
 63  
 
 64  
         // See RFC 2616, section 3.9
 65  0
         String qualityValue = "(?:0(?:\\.\\d{0,3})?|1(?:\\.0{0,3})?)";
 66  
 
 67  
         // See RFC 2616, sections 14.1
 68  0
         String quality = ";\\s*q\\s*=\\s*([^;,]*)";
 69  
 
 70  
         // See RFC 2616, section 3.7
 71  0
         String regex = "(" + token     + ")/(" + token + ")" +
 72  
                 "((?:\\s*" + parameter + ")*)" +
 73  
                 "(?:\\s*"  + quality   + ")?" +
 74  
                 "((?:\\s*" + parameter + ")*)";
 75  
 
 76  0
         tokenPattern      = Pattern.compile(token);
 77  0
         parameterPattern  = Pattern.compile(parameter);
 78  0
         mediaRangePattern = Pattern.compile(regex);
 79  0
         qValuePattern     = Pattern.compile(qualityValue);
 80  0
     }
 81  
 
 82  
     /**
 83  
      * Parses a media type from a string such as <tt>text/html;charset=utf-8;q=0.9</tt>.
 84  
      */
 85  
     public static MediaRangeSpec parseType(String mediaType) {
 86  0
         MediaRangeSpec m = parseRange(mediaType);
 87  0
         if (m == null || m.isWildcardType() || m.isWildcardSubtype()) {
 88  0
             return null;
 89  
         }
 90  0
         return m;
 91  
     }
 92  
 
 93  
     /**
 94  
      * Parses a media range from a string such as <tt>text/*;charset=utf-8;q=0.9</tt>.
 95  
      * Unlike simple media types, media ranges may include wildcards.
 96  
      */
 97  
     public static MediaRangeSpec parseRange(String mediaRange) {
 98  0
         Matcher m = mediaRangePattern.matcher(mediaRange);
 99  0
         if (!m.matches()) {
 100  0
             return null;
 101  
         }
 102  0
         String type = m.group(1).toLowerCase();
 103  0
         String subtype = m.group(2).toLowerCase();
 104  0
         String unparsedParameters = m.group(3);
 105  0
         String qValue = m.group(7);
 106  0
         m = parameterPattern.matcher(unparsedParameters);
 107  0
         if ("*".equals(type) && !"*".equals(subtype)) {
 108  0
             return null;
 109  
         }
 110  0
         List<String> parameterNames = new ArrayList<String>();
 111  0
         List<String> parameterValues = new ArrayList<String>();
 112  0
         while (m.find()) {
 113  0
             String name = m.group(1).toLowerCase();
 114  0
             String value = (m.group(3) == null) ? m.group(2) : unescape(m.group(3));
 115  0
             parameterNames.add(name);
 116  0
             parameterValues.add(value);
 117  0
         }
 118  0
         double quality = 1.0;
 119  0
         if (qValue != null && qValuePattern.matcher(qValue).matches()) {
 120  
             try {
 121  0
                 quality = Double.parseDouble(qValue);
 122  0
             } catch (NumberFormatException ex) {
 123  
                 // quality stays at default value
 124  0
             }
 125  
         }
 126  0
         return new MediaRangeSpec(type, subtype, parameterNames, parameterValues, quality);
 127  
     }
 128  
 
 129  
     /**
 130  
      * Parses an HTTP Accept header into a List of MediaRangeSpecs
 131  
      *
 132  
      * @return A List of MediaRangeSpecs
 133  
      */
 134  
     public static List<MediaRangeSpec> parseAccept(String s) {
 135  0
         List<MediaRangeSpec> result = new ArrayList<MediaRangeSpec>();
 136  0
         Matcher m = mediaRangePattern.matcher(s);
 137  0
         while (m.find()) {
 138  0
             result.add(parseRange(m.group()));
 139  
         }
 140  0
         return result;
 141  
     }
 142  
 
 143  
     private static String unescape(String s) {
 144  0
         return s.replaceAll("\\\\(.)", "$1");
 145  
     }
 146  
 
 147  
     private static String escape(String s) {
 148  0
         return s.replaceAll("[\\\\\"]", "\\\\$0");
 149  
     }
 150  
 
 151  
     private MediaRangeSpec(
 152  
             String type,
 153  
             String subtype,
 154  
             List<String> parameterNames, List<String> parameterValues,
 155  
             double quality
 156  0
     ) {
 157  0
         this.type = type;
 158  0
         this.subtype = subtype;
 159  0
         this.parameterNames = Collections.unmodifiableList(parameterNames);
 160  0
         this.parameterValues = parameterValues;
 161  0
         this.mediaType = buildMediaType();
 162  0
         this.quality = quality;
 163  0
     }
 164  
 
 165  
     private String buildMediaType() {
 166  0
         StringBuffer result = new StringBuffer();
 167  0
         result.append(type);
 168  0
         result.append("/");
 169  0
         result.append(subtype);
 170  0
         for (int i = 0; i < parameterNames.size(); i++) {
 171  0
             result.append(";");
 172  0
             result.append(parameterNames.get(i));
 173  0
             result.append("=");
 174  0
             String value = (String) parameterValues.get(i);
 175  0
             if (tokenPattern.matcher(value).matches()) {
 176  0
                 result.append(value);
 177  
             } else {
 178  0
                 result.append("\"");
 179  0
                 result.append(escape(value));
 180  0
                 result.append("\"");
 181  
             }
 182  
         }
 183  0
         return result.toString();
 184  
     }
 185  
 
 186  
     public String getType() {
 187  0
         return type;
 188  
     }
 189  
 
 190  
     public String getSubtype() {
 191  0
         return subtype;
 192  
     }
 193  
 
 194  
     public String getMediaType() {
 195  0
         return mediaType;
 196  
     }
 197  
 
 198  
     public List<String> getParameterNames() {
 199  0
         return parameterNames;
 200  
     }
 201  
 
 202  
     public String getParameter(String parameterName) {
 203  0
         for (int i = 0; i < parameterNames.size(); i++) {
 204  0
             if (parameterNames.get(i).equals(parameterName.toLowerCase())) {
 205  0
                 return parameterValues.get(i);
 206  
             }
 207  
         }
 208  0
         return null;
 209  
     }
 210  
 
 211  
     public boolean isWildcardType() {
 212  0
         return "*".equals(type);
 213  
     }
 214  
 
 215  
     public boolean isWildcardSubtype() {
 216  0
         return !isWildcardType() && "*".equals(subtype);
 217  
     }
 218  
 
 219  
     public double getQuality() {
 220  0
         return quality;
 221  
     }
 222  
 
 223  
     public int getPrecedence(MediaRangeSpec range) {
 224  0
         if (range.isWildcardType()) return 1;
 225  0
         if (!range.type.equals(type)) return 0;
 226  0
         if (range.isWildcardSubtype()) return 2;
 227  0
         if (!range.subtype.equals(subtype)) return 0;
 228  0
         if (range.getParameterNames().isEmpty()) return 3;
 229  0
         int result = 3;
 230  0
         for (int i = 0; i < range.getParameterNames().size(); i++) {
 231  0
             String name  = range.getParameterNames().get(i);
 232  0
             String value = range.getParameter(name);
 233  0
             if (!value.equals(getParameter(name))) return 0;
 234  0
             result++;
 235  
         }
 236  0
         return result;
 237  
     }
 238  
 
 239  
     public MediaRangeSpec getBestMatch(List<MediaRangeSpec> mediaRanges) {
 240  0
         MediaRangeSpec result = null;
 241  0
         int bestPrecedence = 0;
 242  0
         Iterator<MediaRangeSpec> it = mediaRanges.iterator();
 243  0
         while (it.hasNext()) {
 244  0
             MediaRangeSpec range = it.next();
 245  0
             if (getPrecedence(range) > bestPrecedence) {
 246  0
                 bestPrecedence = getPrecedence(range);
 247  0
                 result = range;
 248  
             }
 249  0
         }
 250  0
         return result;
 251  
     }
 252  
 
 253  
     public String toString() {
 254  0
         return mediaType + ";q=" + quality;
 255  
     }
 256  
 }