| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| I18NTokenizer | 
 | 
 | 2.6;2,6 | ||||
| I18NTokenizer$1 | 
 | 
 | 2.6;2,6 | ||||
| I18NTokenizer$AcceptLanguage | 
 | 
 | 2.6;2,6 | 
| 1 |  package org.codehaus.plexus.i18n; | |
| 2 | ||
| 3 |  /* | |
| 4 |   * Copyright 2001-2007 Codehaus Foundation. | |
| 5 |   * | |
| 6 |   * Licensed under the Apache License, Version 2.0 (the "License"); | |
| 7 |   * you may not use this file except in compliance with the License. | |
| 8 |   * You may obtain a copy of the License at | |
| 9 |   * | |
| 10 |   *      http://www.apache.org/licenses/LICENSE-2.0 | |
| 11 |   * | |
| 12 |   * Unless required by applicable law or agreed to in writing, software | |
| 13 |   * distributed under the License is distributed on an "AS IS" BASIS, | |
| 14 |   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 15 |   * See the License for the specific language governing permissions and | |
| 16 |   * limitations under the License. | |
| 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: I18NTokenizer.java 6675 2007-07-20 23:05:53Z olamy $ | |
| 32 |   * | |
| 33 |   * @todo Move this class out of here as its purely web related. | |
| 34 |   */ | |
| 35 | 3 | public class I18NTokenizer | 
| 36 |      implements Iterator | |
| 37 |  { | |
| 38 |      /** | |
| 39 |       * Separates elements of the <code>Accept-Language</code> HTTP | |
| 40 |       * header. | |
| 41 |       */ | |
| 42 | private static final String LOCALE_SEPARATOR = ","; | |
| 43 | ||
| 44 |      /** | |
| 45 |       * Separates locale from quality within elements. | |
| 46 |       */ | |
| 47 | private static final char QUALITY_SEPARATOR = ';'; | |
| 48 | ||
| 49 |      /** | |
| 50 |       * The default quality value for an <code>AcceptLanguage</code> | |
| 51 |       * object. | |
| 52 |       */ | |
| 53 | 1 | private static final Float DEFAULT_QUALITY = new Float(1.0f); | 
| 54 | ||
| 55 |      /** | |
| 56 |       * The parsed locales. | |
| 57 |       */ | |
| 58 | 1 | private ArrayList locales = new ArrayList(3); | 
| 59 | ||
| 60 |      /** | |
| 61 |       * Parses the <code>Accept-Language</code> header. | |
| 62 |       * | |
| 63 |       * @param header The <code>Accept-Language</code> header | |
| 64 |       * (i.e. <code>en, es;q=0.8, zh-TW;q=0.1</code>). | |
| 65 |       */ | |
| 66 |      public I18NTokenizer(String header) | |
| 67 | 1 |      { | 
| 68 | 1 |          StringTokenizer tok = new StringTokenizer(header, LOCALE_SEPARATOR); | 
| 69 | 4 | while (tok.hasMoreTokens()) | 
| 70 |          { | |
| 71 | 3 |              AcceptLanguage acceptLang = new AcceptLanguage(); | 
| 72 | 3 | String element = tok.nextToken().trim(); | 
| 73 |              int index; | |
| 74 | ||
| 75 |              // Record and cut off any quality value that comes after a | |
| 76 |              // semi-colon. | |
| 77 | 3 | if ( (index = element.indexOf(QUALITY_SEPARATOR)) != -1 ) | 
| 78 |              { | |
| 79 | 2 | String q = element.substring(index); | 
| 80 | 2 | element = element.substring(0, index); | 
| 81 | 2 |                  if ( (index = q.indexOf('=')) != -1 ) | 
| 82 |                  { | |
| 83 |                      try | |
| 84 |                      { | |
| 85 | 2 | acceptLang.quality = | 
| 86 | Float.valueOf(q.substring(index + 1)); | |
| 87 | } | |
| 88 | 0 |                      catch (NumberFormatException useDefault) | 
| 89 |                      { | |
| 90 | 2 | } | 
| 91 | } | |
| 92 | } | |
| 93 | ||
| 94 | 3 | element = element.trim(); | 
| 95 | ||
| 96 |              // Create a Locale from the language.  A dash may separate the | |
| 97 |              // language from the country. | |
| 98 | 3 | if ( (index = element.indexOf('-')) == -1 ) | 
| 99 |              { | |
| 100 |                  // No dash means no country. | |
| 101 | 2 | acceptLang.locale = new Locale(element, ""); | 
| 102 | } | |
| 103 |              else | |
| 104 |              { | |
| 105 | 1 |                  acceptLang.locale = new Locale(element.substring(0, index), | 
| 106 | element.substring(index + 1)); | |
| 107 | } | |
| 108 | ||
| 109 | 3 | locales.add(acceptLang); | 
| 110 | 3 | } | 
| 111 | ||
| 112 |          // Sort by quality in descending order. | |
| 113 | 1 | Collections.sort(locales, Collections.reverseOrder()); | 
| 114 | 1 | } | 
| 115 | ||
| 116 |      /** | |
| 117 |       * @return Whether there are more locales. | |
| 118 |       */ | |
| 119 | public boolean hasNext() | |
| 120 |      { | |
| 121 | 0 |          return !locales.isEmpty(); | 
| 122 | } | |
| 123 | ||
| 124 |      /** | |
| 125 |       * Creates a <code>Locale</code> from the next element of the | |
| 126 |       * <code>Accept-Language</code> header. | |
| 127 |       * | |
| 128 |       * @return The next highest-rated <code>Locale</code>. | |
| 129 |       * @throws NoSuchElementException No more locales. | |
| 130 |       */ | |
| 131 |      public Object next() | |
| 132 |      { | |
| 133 | 3 |          if (locales.isEmpty()) | 
| 134 |          { | |
| 135 | 0 |              throw new NoSuchElementException(); | 
| 136 | } | |
| 137 | 3 |          return ((AcceptLanguage) locales.remove(0)).locale; | 
| 138 | } | |
| 139 | ||
| 140 |      /** | |
| 141 |       * Not implemented. | |
| 142 |       */ | |
| 143 | public final void remove() | |
| 144 |      { | |
| 145 | 0 |          throw new UnsupportedOperationException(getClass().getName() + | 
| 146 |                                                  " does not support remove()"); | |
| 147 | } | |
| 148 | ||
| 149 |      /** | |
| 150 |       * Struct representing an element of the HTTP | |
| 151 |       * <code>Accept-Language</code> header. | |
| 152 |       */ | |
| 153 | 6 | private class AcceptLanguage implements Comparable | 
| 154 |      { | |
| 155 |          /** | |
| 156 |           * The language and country. | |
| 157 |           */ | |
| 158 | Locale locale; | |
| 159 | ||
| 160 |          /** | |
| 161 |           * The quality of our locale (as values approach | |
| 162 |           * <code>1.0</code>, they indicate increased user preference). | |
| 163 |           */ | |
| 164 | 3 | Float quality = DEFAULT_QUALITY; | 
| 165 | ||
| 166 | public final int compareTo(Object acceptLang) | |
| 167 |          { | |
| 168 | 2 |              return quality.compareTo( ((AcceptLanguage) acceptLang).quality ); | 
| 169 | } | |
| 170 | } | |
| 171 | } |