##### 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.0;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 |  |
     }