1 package org.apache.turbine.services.localization;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.Iterator;
22 import java.util.Locale;
23 import java.util.NoSuchElementException;
24 import java.util.StringTokenizer;
25
26 /***
27 * Parses the HTTP <code>Accept-Language</code> header as per section
28 * 14.4 of RFC 2068 (HTTP 1.1 header field definitions).
29 *
30 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
31 * @version $Id: LocaleTokenizer.java 264148 2005-08-29 14:21:04Z henning $
32 */
33 public class LocaleTokenizer
34 implements Iterator
35 {
36 /***
37 * Separates elements of the <code>Accept-Language</code> HTTP
38 * header.
39 */
40 private static final String LOCALE_SEPARATOR = ",";
41
42 /***
43 * Separates locale from quality within elements.
44 */
45 private static final char QUALITY_SEPARATOR = ';';
46
47 /***
48 * The default quality value for an <code>AcceptLanguage</code>
49 * object.
50 */
51 private static final Float DEFAULT_QUALITY = new Float(1.0f);
52
53 /***
54 * The parsed locales.
55 */
56 private ArrayList locales = new ArrayList(3);
57
58 /***
59 * Parses the <code>Accept-Language</code> header.
60 *
61 * @param header The <code>Accept-Language</code> header
62 * (i.e. <code>en, es;q=0.8, zh-TW;q=0.1</code>).
63 */
64 public LocaleTokenizer(String header)
65 {
66 StringTokenizer tok = new StringTokenizer(header, LOCALE_SEPARATOR);
67 while (tok.hasMoreTokens())
68 {
69 AcceptLanguage acceptLang = new AcceptLanguage();
70 String element = tok.nextToken().trim();
71 int index;
72
73
74
75 if ((index = element.indexOf(QUALITY_SEPARATOR)) != -1)
76 {
77 String q = element.substring(index);
78 element = element.substring(0, index);
79 if ((index = q.indexOf('=')) != -1)
80 {
81 try
82 {
83 acceptLang.quality =
84 Float.valueOf(q.substring(index + 1));
85 }
86 catch (NumberFormatException useDefault)
87 {
88 }
89 }
90 }
91
92 element = element.trim();
93
94
95
96 if ((index = element.indexOf('-')) == -1)
97 {
98
99 acceptLang.locale = new Locale(element, "");
100 }
101 else
102 {
103 acceptLang.locale = new Locale(element.substring(0, index),
104 element.substring(index + 1));
105 }
106
107 locales.add(acceptLang);
108 }
109
110
111 Collections.sort(locales, Collections.reverseOrder());
112 }
113
114 /***
115 * @return Whether there are more locales.
116 */
117 public boolean hasNext()
118 {
119 return !locales.isEmpty();
120 }
121
122 /***
123 * Creates a <code>Locale</code> from the next element of the
124 * <code>Accept-Language</code> header.
125 *
126 * @return The next highest-rated <code>Locale</code>.
127 * @throws NoSuchElementException No more locales.
128 */
129 public Object next()
130 {
131 if (locales.isEmpty())
132 {
133 throw new NoSuchElementException();
134 }
135 return ((AcceptLanguage) locales.remove(0)).locale;
136 }
137
138 /***
139 * Not implemented.
140 */
141 public final void remove()
142 {
143 throw new UnsupportedOperationException(getClass().getName() +
144 " does not support remove()");
145 }
146
147 /***
148 * Struct representing an element of the HTTP
149 * <code>Accept-Language</code> header.
150 */
151 private class AcceptLanguage implements Comparable
152 {
153 /***
154 * The language and country.
155 */
156 Locale locale;
157
158 /***
159 * The quality of our locale (as values approach
160 * <code>1.0</code>, they indicate increased user preference).
161 */
162 Float quality = DEFAULT_QUALITY;
163
164 public final int compareTo(Object acceptLang)
165 {
166 return quality.compareTo(((AcceptLanguage) acceptLang).quality);
167 }
168 }
169 }