001: package org.apache.turbine.services.localization;
002:
003: /*
004: * Copyright 2001-2005 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License")
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.Iterator;
022: import java.util.Locale;
023: import java.util.NoSuchElementException;
024: import java.util.StringTokenizer;
025:
026: /**
027: * Parses the HTTP <code>Accept-Language</code> header as per section
028: * 14.4 of RFC 2068 (HTTP 1.1 header field definitions).
029: *
030: * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
031: * @version $Id: LocaleTokenizer.java 264148 2005-08-29 14:21:04Z henning $
032: */
033: public class LocaleTokenizer implements Iterator {
034: /**
035: * Separates elements of the <code>Accept-Language</code> HTTP
036: * header.
037: */
038: private static final String LOCALE_SEPARATOR = ",";
039:
040: /**
041: * Separates locale from quality within elements.
042: */
043: private static final char QUALITY_SEPARATOR = ';';
044:
045: /**
046: * The default quality value for an <code>AcceptLanguage</code>
047: * object.
048: */
049: private static final Float DEFAULT_QUALITY = new Float(1.0f);
050:
051: /**
052: * The parsed locales.
053: */
054: private ArrayList locales = new ArrayList(3);
055:
056: /**
057: * Parses the <code>Accept-Language</code> header.
058: *
059: * @param header The <code>Accept-Language</code> header
060: * (i.e. <code>en, es;q=0.8, zh-TW;q=0.1</code>).
061: */
062: public LocaleTokenizer(String header) {
063: StringTokenizer tok = new StringTokenizer(header,
064: LOCALE_SEPARATOR);
065: while (tok.hasMoreTokens()) {
066: AcceptLanguage acceptLang = new AcceptLanguage();
067: String element = tok.nextToken().trim();
068: int index;
069:
070: // Record and cut off any quality value that comes after a
071: // semi-colon.
072: if ((index = element.indexOf(QUALITY_SEPARATOR)) != -1) {
073: String q = element.substring(index);
074: element = element.substring(0, index);
075: if ((index = q.indexOf('=')) != -1) {
076: try {
077: acceptLang.quality = Float.valueOf(q
078: .substring(index + 1));
079: } catch (NumberFormatException useDefault) {
080: }
081: }
082: }
083:
084: element = element.trim();
085:
086: // Create a Locale from the language. A dash may separate the
087: // language from the country.
088: if ((index = element.indexOf('-')) == -1) {
089: // No dash means no country.
090: acceptLang.locale = new Locale(element, "");
091: } else {
092: acceptLang.locale = new Locale(element.substring(0,
093: index), element.substring(index + 1));
094: }
095:
096: locales.add(acceptLang);
097: }
098:
099: // Sort by quality in descending order.
100: Collections.sort(locales, Collections.reverseOrder());
101: }
102:
103: /**
104: * @return Whether there are more locales.
105: */
106: public boolean hasNext() {
107: return !locales.isEmpty();
108: }
109:
110: /**
111: * Creates a <code>Locale</code> from the next element of the
112: * <code>Accept-Language</code> header.
113: *
114: * @return The next highest-rated <code>Locale</code>.
115: * @throws NoSuchElementException No more locales.
116: */
117: public Object next() {
118: if (locales.isEmpty()) {
119: throw new NoSuchElementException();
120: }
121: return ((AcceptLanguage) locales.remove(0)).locale;
122: }
123:
124: /**
125: * Not implemented.
126: */
127: public final void remove() {
128: throw new UnsupportedOperationException(getClass().getName()
129: + " does not support remove()");
130: }
131:
132: /**
133: * Struct representing an element of the HTTP
134: * <code>Accept-Language</code> header.
135: */
136: private class AcceptLanguage implements Comparable {
137: /**
138: * The language and country.
139: */
140: Locale locale;
141:
142: /**
143: * The quality of our locale (as values approach
144: * <code>1.0</code>, they indicate increased user preference).
145: */
146: Float quality = DEFAULT_QUALITY;
147:
148: public final int compareTo(Object acceptLang) {
149: return quality
150: .compareTo(((AcceptLanguage) acceptLang).quality);
151: }
152: }
153: }
|