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