1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
29
30 public class ContentTypeNegotiator {
31
32 private List<VariantSpec> variantSpecs = new ArrayList<>();
33
34 private List<MediaRangeSpec> defaultAcceptRanges = Collections.singletonList(MediaRangeSpec.parseRange("*/*"));
35
36 private Collection<AcceptHeaderOverride> userAgentOverrides = new ArrayList<>();
37
38 protected ContentTypeNegotiator(){}
39
40
41
42
43
44
45
46
47 public MediaRangeSpec getBestMatch(String accept) {
48 return getBestMatch(accept, null);
49 }
50
51
52
53
54
55
56
57
58
59
60 public MediaRangeSpec getBestMatch(String accept, String userAgent) {
61 if (userAgent == null) {
62 userAgent = "";
63 }
64 Iterator<AcceptHeaderOverride> it = userAgentOverrides.iterator();
65 String overriddenAccept = accept;
66 while (it.hasNext()) {
67 AcceptHeaderOverride override = it.next();
68 if (override.matches(accept, userAgent)) {
69 overriddenAccept = override.getReplacement();
70 }
71 }
72 return new Negotiation(toAcceptRanges(overriddenAccept)).negotiate();
73 }
74
75 protected VariantSpec addVariant(String mediaType) {
76 VariantSpec result = new VariantSpec(mediaType);
77 variantSpecs.add(result);
78 return result;
79 }
80
81
82
83
84
85
86
87 protected void setDefaultAccept(String accept) {
88 this.defaultAcceptRanges = MediaRangeSpec.parseAccept(accept);
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102 protected void addUserAgentOverride(
103 Pattern userAgentString,
104 String originalAcceptHeader,
105 String newAcceptHeader
106 ) {
107 this.userAgentOverrides.add(
108 new AcceptHeaderOverride(userAgentString, originalAcceptHeader, newAcceptHeader)
109 );
110 }
111
112 private List<MediaRangeSpec> toAcceptRanges(String accept) {
113 if (accept == null) {
114 return defaultAcceptRanges;
115 }
116 List<MediaRangeSpec> result = MediaRangeSpec.parseAccept(accept);
117 if (result.isEmpty()) {
118 return defaultAcceptRanges;
119 }
120 return result;
121 }
122
123 protected class VariantSpec {
124
125 private MediaRangeSpec type;
126 private List<MediaRangeSpec> aliases = new ArrayList<>();
127 private boolean isDefault = false;
128
129 public VariantSpec(String mediaType) {
130 type = MediaRangeSpec.parseType(mediaType);
131 }
132
133 public VariantSpec addAliasMediaType(String mediaType) {
134 aliases.add(MediaRangeSpec.parseType(mediaType));
135 return this;
136 }
137
138 public void makeDefault() {
139 isDefault = true;
140 }
141
142 public MediaRangeSpec getMediaType() {
143 return type;
144 }
145
146 public boolean isDefault() {
147 return isDefault;
148 }
149
150 public List<MediaRangeSpec> getAliases() {
151 return aliases;
152 }
153 }
154
155 private class Negotiation {
156
157 private final List<MediaRangeSpec> ranges;
158 private MediaRangeSpec bestMatchingVariant = null;
159 private MediaRangeSpec bestDefaultVariant = null;
160 private double bestMatchingQuality = 0;
161 private double bestDefaultQuality = 0;
162
163 Negotiation(List<MediaRangeSpec> ranges) {
164 this.ranges = ranges;
165 }
166
167 MediaRangeSpec negotiate() {
168 Iterator<VariantSpec> it = variantSpecs.iterator();
169 while (it.hasNext()) {
170 VariantSpec variant = it.next();
171 if (variant.isDefault) {
172 evaluateDefaultVariant(variant.getMediaType());
173 }
174 evaluateVariant(variant.getMediaType());
175 Iterator<MediaRangeSpec> aliasIt = variant.getAliases().iterator();
176 while (aliasIt.hasNext()) {
177 MediaRangeSpec alias = aliasIt.next();
178 evaluateVariantAlias(alias, variant.getMediaType());
179 }
180 }
181 return (bestMatchingVariant == null) ? bestDefaultVariant : bestMatchingVariant;
182 }
183
184 private void evaluateVariantAlias(MediaRangeSpec./../../org/apache/any23/servlet/conneg/MediaRangeSpec.html#MediaRangeSpec">MediaRangeSpec variant, MediaRangeSpec isAliasFor) {
185 if (variant.getBestMatch(ranges) == null)
186 return;
187 double q = variant.getBestMatch(ranges).getQuality();
188 if (q * variant.getQuality() > bestMatchingQuality) {
189 bestMatchingVariant = isAliasFor;
190 bestMatchingQuality = q * variant.getQuality();
191 }
192 }
193
194 private void evaluateVariant(MediaRangeSpec variant) {
195 evaluateVariantAlias(variant, variant);
196 }
197
198 private void evaluateDefaultVariant(MediaRangeSpec variant) {
199 if (variant.getQuality() > bestDefaultQuality) {
200 bestDefaultVariant = variant;
201 bestDefaultQuality = 0.00001 * variant.getQuality();
202 }
203 }
204
205 }
206
207 private class AcceptHeaderOverride {
208
209 private Pattern userAgentPattern;
210 private String original;
211 private String replacement;
212
213 AcceptHeaderOverride(Pattern userAgentPattern, String original, String replacement) {
214 this.userAgentPattern = userAgentPattern;
215 this.original = original;
216 this.replacement = replacement;
217 }
218
219 boolean matches(String acceptHeader, String userAgentHeader) {
220 return (userAgentPattern == null
221 || userAgentPattern.matcher(userAgentHeader).find())
222 && (original == null || original.equals(acceptHeader));
223 }
224
225 String getReplacement() {
226 return replacement;
227 }
228 }
229
230 }