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