001: /*
002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/i18n/CmsAcceptLanguageHeaderParser.java,v $
003: * Date : $Date: 2008-02-27 12:05:47 $
004: * Version: $Revision: 1.19 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: *
031: * This file is based on:
032: * org.apache.fulcrum.localization.LocaleTokenizer
033: * from the Apache Fulcrum/Turbine project.
034: *
035: * The Apache Software License, Version 1.1
036: *
037: * Copyright (c) 2001 The Apache Software Foundation. All rights
038: * reserved.
039: *
040: * Redistribution and use in source and binary forms, with or without
041: * modification, are permitted provided that the following conditions
042: * are met:
043: *
044: * 1. Redistributions of source code must retain the above copyright
045: * notice, this list of conditions and the following disclaimer.
046: *
047: * 2. Redistributions in binary form must reproduce the above copyright
048: * notice, this list of conditions and the following disclaimer in
049: * the documentation and/or other materials provided with the
050: * distribution.
051: *
052: * 3. The end-user documentation included with the redistribution,
053: * if any, must include the following acknowledgment:
054: * "This product includes software developed by the
055: * Apache Software Foundation (http://www.apache.org/)."
056: * Alternately, this acknowledgment may appear in the software itself,
057: * if and wherever such third-party acknowledgments normally appear.
058: *
059: * 4. The names "Apache" and "Apache Software Foundation" and
060: * "Apache Turbine" must not be used to endorse or promote products
061: * derived from this software without prior written permission. For
062: * written permission, please contact apache@apache.org.
063: *
064: * 5. Products derived from this software may not be called "Apache",
065: * "Apache Turbine", nor may "Apache" appear in their name, without
066: * prior written permission of the Apache Software Foundation.
067: *
068: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
069: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
070: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
071: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
072: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
073: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
074: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
075: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
076: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
077: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
078: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
079: * SUCH DAMAGE.
080: * ====================================================================
081: *
082: * This software consists of voluntary contributions made by many
083: * individuals on behalf of the Apache Software Foundation. For more
084: * information on the Apache Software Foundation, please see
085: * <http://www.apache.org/>.
086: */
087:
088: package org.opencms.i18n;
089:
090: import org.opencms.main.CmsIllegalArgumentException;
091: import org.opencms.main.CmsRuntimeException;
092: import org.opencms.main.OpenCms;
093: import org.opencms.util.CmsStringUtil;
094:
095: import java.util.ArrayList;
096: import java.util.Collections;
097: import java.util.Iterator;
098: import java.util.List;
099: import java.util.Locale;
100: import java.util.NoSuchElementException;
101:
102: import javax.servlet.http.HttpServletRequest;
103:
104: /**
105: * Parses the HTTP <code>Accept-Language</code> header as per section 14.4 of RFC 2068
106: * (HTTP 1.1 header field definitions) and creates a sorted list of Locales from it.
107: *
108: * @author Daniel Rall
109: * @author Alexander Kandzior
110: *
111: * @version $Revision: 1.19 $
112: *
113: * @since 6.0.0
114: */
115: public class CmsAcceptLanguageHeaderParser implements Iterator {
116:
117: /**
118: * Struct representing an element of the HTTP <code>Accept-Language</code> header.
119: */
120: protected static class AcceptLanguage implements Comparable {
121:
122: /** The language and country. */
123: Locale m_locale;
124:
125: /** The m_quality of our m_locale (as values approach <code>1.0</code>, they indicate increased user preference). */
126: Float m_quality = DEFAULT_QUALITY;
127:
128: /**
129: * @see java.lang.Comparable#compareTo(java.lang.Object)
130: */
131: public final int compareTo(Object acceptLang) {
132:
133: return m_quality
134: .compareTo(((AcceptLanguage) acceptLang).m_quality);
135: }
136:
137: /**
138: * @see java.lang.Object#equals(java.lang.Object)
139: */
140: public boolean equals(Object obj) {
141:
142: if (obj == this ) {
143: return true;
144: }
145: if (obj instanceof AcceptLanguage) {
146: AcceptLanguage other = (AcceptLanguage) obj;
147: return m_locale.equals(other.m_locale)
148: && (m_quality.floatValue() == other.m_quality
149: .floatValue());
150: }
151: return false;
152: }
153:
154: /**
155: * @see java.lang.Object#hashCode()
156: */
157: public int hashCode() {
158:
159: return m_locale.hashCode()
160: * (int) (m_quality.floatValue() * 1117.0);
161: }
162: }
163:
164: /** A constant for the HTTP <code>Accept-Language</code> header. */
165: public static final String ACCEPT_LANGUAGE = "Accept-Language";
166:
167: /** The default m_quality value for an <code>AcceptLanguage</code> object. */
168: protected static final Float DEFAULT_QUALITY = new Float(1.0f);
169:
170: /** Separates elements of the <code>Accept-Language</code> HTTP header. */
171: private static final char LOCALE_SEPARATOR = ',';
172:
173: /** Separates m_locale from m_quality within elements. */
174: private static final char QUALITY_SEPARATOR = ';';
175:
176: /** The parsed <code>Accept-Language</code> headers. */
177: private List m_acceptLanguage = new ArrayList(3);
178:
179: /** The parsed locales. */
180: private List m_locales;
181:
182: /**
183: * Parses the <code>Accept-Language</code> header from the provided request.<p>
184: *
185: * @param req the request to parse
186: * @param defaultLocale the default locale to use
187: */
188: public CmsAcceptLanguageHeaderParser(HttpServletRequest req,
189: Locale defaultLocale) {
190:
191: this (req.getHeader(ACCEPT_LANGUAGE), defaultLocale);
192: }
193:
194: /**
195: * Parses the <code>Accept-Language</code> header.<p>
196: *
197: * @param header the <code>Accept-Language</code> header (i.e. <code>en, es;q=0.8, zh-TW;q=0.1</code>)
198: * @param defaultLocale the default locale to use
199: */
200: public CmsAcceptLanguageHeaderParser(String header,
201: Locale defaultLocale) {
202:
203: // check if there was a locale foud in the HTTP header.
204: // if not, use the default locale.
205: if (header == null) {
206: m_locales = new ArrayList();
207: m_locales.add(defaultLocale);
208: } else {
209: List tokens = CmsStringUtil.splitAsList(header,
210: LOCALE_SEPARATOR, true);
211: Iterator it = tokens.iterator();
212: while (it.hasNext()) {
213: AcceptLanguage acceptLang = new AcceptLanguage();
214: String element = (String) it.next();
215: int index;
216:
217: // Record and cut off any quality value that comes after a semi-colon.
218: index = element.indexOf(QUALITY_SEPARATOR);
219: if (index != -1) {
220: String q = element.substring(index);
221: element = element.substring(0, index);
222: index = q.indexOf('=');
223: if (index != -1) {
224: try {
225: acceptLang.m_quality = Float.valueOf(q
226: .substring(index + 1));
227: } catch (NumberFormatException useDefault) {
228: // noop
229: }
230: }
231: }
232:
233: element = element.trim();
234:
235: // Create a Locale from the language. A dash may separate the language from the country.
236: index = element.indexOf('-');
237: if (index == -1) {
238: // No dash means no country.
239: acceptLang.m_locale = new Locale(element, "");
240: } else {
241: acceptLang.m_locale = new Locale(element.substring(
242: 0, index), element.substring(index + 1));
243: }
244:
245: m_acceptLanguage.add(acceptLang);
246: }
247:
248: // sort by quality in descending order
249: Collections.sort(m_acceptLanguage, Collections
250: .reverseOrder());
251:
252: // store all calculated Locales in a List
253: m_locales = new ArrayList(m_acceptLanguage.size());
254: Iterator i = m_acceptLanguage.iterator();
255: while (i.hasNext()) {
256: AcceptLanguage lang = (AcceptLanguage) i.next();
257: m_locales.add(lang.m_locale);
258: }
259: }
260:
261: }
262:
263: /**
264: * Creates a value string for the HTTP Accept-Language header based on the default localed.<p>
265: *
266: * @return value string for the HTTP Accept-Language
267: */
268: public static String createLanguageHeader() {
269:
270: String header;
271:
272: // get the default accept-language header value
273: List defaultLocales = OpenCms.getLocaleManager()
274: .getDefaultLocales();
275: Iterator i = defaultLocales.iterator();
276: header = "";
277: while (i.hasNext()) {
278: Locale loc = (Locale) i.next();
279: header += loc.getLanguage() + ", ";
280: }
281: header = header.substring(0, header.length() - 2);
282: return header;
283: }
284:
285: /**
286: * Returns the sorted list of accepted Locales.<p>
287: *
288: * @return the sorted list of accepted Locales
289: */
290: public List getAcceptedLocales() {
291:
292: return m_locales;
293: }
294:
295: /**
296: * @return Whether there are more locales.
297: */
298: public boolean hasNext() {
299:
300: return !m_acceptLanguage.isEmpty();
301: }
302:
303: /**
304: * Creates a <code>Locale</code> from the next element of the <code>Accept-Language</code> header.
305: *
306: * @return The next highest-rated <code>Locale</code>.
307: */
308: public Object next() {
309:
310: if (m_acceptLanguage.isEmpty()) {
311: throw new NoSuchElementException();
312: }
313: return ((AcceptLanguage) m_acceptLanguage.remove(0)).m_locale;
314: }
315:
316: /**
317: * Not implemented.
318: *
319: * @throws CmsIllegalArgumentException always to signal that remove is not implemented
320: * (<b>interface contract defines {@link UnsupportedOperationException}</b>)
321: */
322: public final void remove() throws CmsIllegalArgumentException {
323:
324: throw new CmsRuntimeException(
325: org.opencms.db.Messages
326: .get()
327: .container(
328: org.opencms.db.Messages.ERR_UNSUPPORTED_OPERATION_2,
329: getClass().getName(), "remove()"));
330: }
331: }
|