##### Coverage Report - org.apache.any23.servlet.conneg.ContentTypeNegotiator

Classes in this File | Line Coverage | Branch Coverage | Complexity
ContentTypeNegotiator |
0% | 0/29
0% | 0/10
| 1.8181818181818181;1.818
ContentTypeNegotiator$AcceptHeaderOverride |
0% | 0/8
0% | 0/8
| 1.8181818181818181;1.818
ContentTypeNegotiator$Negotiation |
0% | 0/32
0% | 0/14
| 1.8181818181818181;1.818
ContentTypeNegotiator$VariantSpec |
0% | 0/13
N/A | N/A
| 1.8181818181818181;1.818

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.Collection;
22 |  |
     import java.util.Collections;
23 |  |
     import java.util.Iterator;
24 |  |
     import java.util.List;
25 |  |
     import java.util.regex.Pattern;
26 |  |
27 |  |
     /**
28 |  |
      * This class defines a negotiator for content types based on scoring.
29 |  |
      */
30 | 0 |
     public class ContentTypeNegotiator {
31 |  |
32 | 0 |
         private List<VariantSpec> variantSpecs = new ArrayList<VariantSpec>();
33 |  |
34 | 0 |
         private List<MediaRangeSpec> defaultAcceptRanges = Collections.singletonList(MediaRangeSpec.parseRange("*/*"));
35 |  |
36 | 0 |
         private Collection<AcceptHeaderOverride> userAgentOverrides = new ArrayList<AcceptHeaderOverride>();
37 |  |
38 | 0 |
         protected ContentTypeNegotiator(){}
39 |  |
40 |  |
         /**
41 |  |
          * Returns the {@link MediaRangeSpec}
42 |  |
          * associated to the given <i>accept</i> type.
43 |  |
          *
44 |  |
          * @param accept
45 |  |
          * @return a {@link MediaRangeSpec} associated to the accept parameter
46 |  |
          */
47 |  |
         public MediaRangeSpec getBestMatch(String accept) {
48 | 0 |
             return getBestMatch(accept, null);
49 |  |
         }
50 |  |
51 |  |
         /**
52 |  |
          * Returns the {@link MediaRangeSpec}
53 |  |
          * associated to the given <i>accept</i> type and <i>userAgent</i>.
54 |  |
          *
55 |  |
          * @param accept
56 |  |
          * @param userAgent
57 |  |
          * @return the {@link MediaRangeSpec}
58 |  |
          * associated to the given <i>accept</i> type and <i>userAgent</i>.
59 |  |
          */
60 |  |
         public MediaRangeSpec getBestMatch(String accept, String userAgent) {
61 | 0 |
             if (userAgent == null) {
62 | 0 |
                 userAgent = "";
63 |  |
             }
64 | 0 |
             Iterator<AcceptHeaderOverride> it = userAgentOverrides.iterator();
65 | 0 |
             String overriddenAccept = accept;
66 | 0 |
             while (it.hasNext()) {
67 | 0 |
                 AcceptHeaderOverride override = it.next();
68 | 0 |
                 if (override.matches(accept, userAgent)) {
69 | 0 |
                     overriddenAccept = override.getReplacement();
70 |  |
                 }
71 | 0 |
             }
72 | 0 |
             return new Negotiation(toAcceptRanges(overriddenAccept)).negotiate();
73 |  |
         }
74 |  |
75 |  |
         protected VariantSpec addVariant(String mediaType) {
76 | 0 |
             VariantSpec result = new VariantSpec(mediaType);
77 | 0 |
             variantSpecs.add(result);
78 | 0 |
             return result;
79 |  |
         }
80 |  |
81 |  |
         /**
82 |  |
          * Sets an Accept header to be used as the default if a client does
83 |  |
          * not send an Accept header, or if the Accept header cannot be parsed.
84 |  |
          * Defaults to "* / *".
85 |  |
          */
86 |  |
         protected void setDefaultAccept(String accept) {
87 | 0 |
             this.defaultAcceptRanges = MediaRangeSpec.parseAccept(accept);
88 | 0 |
         }
89 |  |
90 |  |
         /**
91 |  |
          * Overrides the Accept header for certain user agents. This can be
92 |  |
          * used to implement special-case handling for user agents that send
93 |  |
          * faulty Accept headers.
94 |  |
          *
95 |  |
          * @param userAgentString      A pattern to be matched against the User-Agent header;
96 |  |
          *                             <tt>null</tt> means regardless of User-Agent
97 |  |
          * @param originalAcceptHeader Only override the Accept header if the user agent
98 |  |
          *                             sends this header; <tt>null</tt> means always override
99 |  |
          * @param newAcceptHeader      The Accept header to be used instead
100 |  |
          */
101 |  |
         protected void addUserAgentOverride(
102 |  |
              Pattern userAgentString,
103 |  |
              String originalAcceptHeader,
104 |  |
              String newAcceptHeader
105 |  |
         ) {
106 | 0 |
             this.userAgentOverrides.add(
107 |  |
                 new AcceptHeaderOverride(userAgentString, originalAcceptHeader, newAcceptHeader)
108 |  |
             );
109 | 0 |
         }
110 |  |
111 |  |
         private List<MediaRangeSpec> toAcceptRanges(String accept) {
112 | 0 |
             if (accept == null) {
113 | 0 |
                 return defaultAcceptRanges;
114 |  |
             }
115 | 0 |
             List<MediaRangeSpec> result = MediaRangeSpec.parseAccept(accept);
116 | 0 |
             if (result.isEmpty()) {
117 | 0 |
                 return defaultAcceptRanges;
118 |  |
             }
119 | 0 |
             return result;
120 |  |
         }
121 |  |
122 | 0 |
         protected class VariantSpec {
123 |  |
124 |  |
             private MediaRangeSpec type;
125 | 0 |
             private List<MediaRangeSpec> aliases = new ArrayList<MediaRangeSpec>();
126 | 0 |
             private boolean isDefault = false;
127 |  |
128 | 0 |
             public VariantSpec(String mediaType) {
129 | 0 |
                 type = MediaRangeSpec.parseType(mediaType);
130 | 0 |
             }
131 |  |
132 |  |
             public VariantSpec addAliasMediaType(String mediaType) {
133 | 0 |
                 aliases.add(MediaRangeSpec.parseType(mediaType));
134 | 0 |
                 return this;
135 |  |
             }
136 |  |
137 |  |
             public void makeDefault() {
138 | 0 |
                 isDefault = true;
139 | 0 |
             }
140 |  |
141 |  |
             public MediaRangeSpec getMediaType() {
142 | 0 |
                 return type;
143 |  |
             }
144 |  |
145 |  |
             public boolean isDefault() {
146 | 0 |
                 return isDefault;
147 |  |
             }
148 |  |
149 |  |
             public List<MediaRangeSpec> getAliases() {
150 | 0 |
                 return aliases;
151 |  |
             }
152 |  |
         }
153 |  |
154 |  |
         private class Negotiation {
155 |  |
156 |  |
             private final List<MediaRangeSpec> ranges;
157 | 0 |
             private MediaRangeSpec bestMatchingVariant = null;
158 | 0 |
             private MediaRangeSpec bestDefaultVariant = null;
159 | 0 |
             private double bestMatchingQuality = 0;
160 | 0 |
             private double bestDefaultQuality = 0;
161 |  |
162 | 0 |
             Negotiation(List<MediaRangeSpec> ranges) {
163 | 0 |
                 this.ranges = ranges;
164 | 0 |
             }
165 |  |
166 |  |
             MediaRangeSpec negotiate() {
167 | 0 |
                 Iterator<VariantSpec> it = variantSpecs.iterator();
168 | 0 |
                 while (it.hasNext()) {
169 | 0 |
                     VariantSpec variant = it.next();
170 | 0 |
                     if (variant.isDefault) {
171 | 0 |
                         evaluateDefaultVariant(variant.getMediaType());
172 |  |
                     }
173 | 0 |
                     evaluateVariant(variant.getMediaType());
174 | 0 |
                     Iterator<MediaRangeSpec> aliasIt = variant.getAliases().iterator();
175 | 0 |
                     while (aliasIt.hasNext()) {
176 | 0 |
                         MediaRangeSpec alias = aliasIt.next();
177 | 0 |
                         evaluateVariantAlias(alias, variant.getMediaType());
178 | 0 |
                     }
179 | 0 |
                 }
180 | 0 |
                 return (bestMatchingVariant == null) ? bestDefaultVariant : bestMatchingVariant;
181 |  |
             }
182 |  |
183 |  |
             private void evaluateVariantAlias(MediaRangeSpec variant, MediaRangeSpec isAliasFor) {
184 | 0 |
                 if (variant.getBestMatch(ranges) == null) return;
185 | 0 |
                 double q = variant.getBestMatch(ranges).getQuality();
186 | 0 |
                 if (q * variant.getQuality() > bestMatchingQuality) {
187 | 0 |
                     bestMatchingVariant = isAliasFor;
188 | 0 |
                     bestMatchingQuality = q * variant.getQuality();
189 |  |
                 }
190 | 0 |
             }
191 |  |
192 |  |
             private void evaluateVariant(MediaRangeSpec variant) {
193 | 0 |
                 evaluateVariantAlias(variant, variant);
194 | 0 |
             }
195 |  |
196 |  |
             private void evaluateDefaultVariant(MediaRangeSpec variant) {
197 | 0 |
                 if (variant.getQuality() > bestDefaultQuality) {
198 | 0 |
                     bestDefaultVariant = variant;
199 | 0 |
                     bestDefaultQuality = 0.00001 * variant.getQuality();
200 |  |
                 }
201 | 0 |
             }
202 |  |
203 |  |
         }
204 |  |
205 |  |
         private class AcceptHeaderOverride {
206 |  |
207 |  |
             private Pattern userAgentPattern;
208 |  |
             private String original;
209 |  |
             private String replacement;
210 |  |
211 | 0 |
             AcceptHeaderOverride(Pattern userAgentPattern, String original, String replacement) {
212 | 0 |
                 this.userAgentPattern = userAgentPattern;
213 | 0 |
                 this.original = original;
214 | 0 |
                 this.replacement = replacement;
215 | 0 |
             }
216 |  |
217 |  |
             boolean matches(String acceptHeader) {
218 | 0 |
                 return matches(acceptHeader, null);
219 |  |
             }
220 |  |
221 |  |
             boolean matches(String acceptHeader, String userAgentHeader) {
222 | 0 |
                 return (userAgentPattern == null
223 |  |
                         || userAgentPattern.matcher(userAgentHeader).find())
224 |  |
                         && (original == null || original.equals(acceptHeader));
225 |  |
             }
226 |  |
227 |  |
             String getReplacement() {
228 | 0 |
                 return replacement;
229 |  |
             }
230 |  |
         }
231 |  |
232 |  |
     }