View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.codec.language.bm;
19  
20  import java.io.InputStream;
21  import java.util.Collections;
22  import java.util.EnumMap;
23  import java.util.HashSet;
24  import java.util.Map;
25  import java.util.NoSuchElementException;
26  import java.util.Scanner;
27  import java.util.Set;
28  
29  /**
30   * Language codes.
31   * <p>
32   * Language codes are typically loaded from resource files. These are UTF-8 encoded text files. They are
33   * systematically named following the pattern:
34   * <blockquote>org/apache/commons/codec/language/bm/${{@link NameType#getName()} languages.txt</blockquote>
35   * <p>
36   * The format of these resources is the following:
37   * <ul>
38   * <li><b>Language:</b> a single string containing no whitespace</li>
39   * <li><b>End-of-line comments:</b> Any occurrence of '//' will cause all text following on that line to be
40   * discarded as a comment.</li>
41   * <li><b>Multi-line comments:</b> Any line starting with '/*' will start multi-line commenting mode.
42   * This will skip all content until a line ending in '*' and '/' is found.</li>
43   * <li><b>Blank lines:</b> All blank lines will be skipped.</li>
44   * </ul>
45   * <p>
46   * Ported from language.php
47   * <p>
48   * This class is immutable and thread-safe.
49   *
50   * @since 1.6
51   * @version $Id: Languages.html 889935 2013-12-11 05:05:13Z ggregory $
52   */
53  public class Languages {
54      // Iimplementation note: This class is divided into two sections. The first part is a static factory interface that
55      // exposes org/apache/commons/codec/language/bm/%s_languages.txt for %s in NameType.* as a list of supported
56      // languages, and a second part that provides instance methods for accessing this set fo supported languages.
57  
58      /**
59       * A set of languages.
60       */
61      public static abstract class LanguageSet {
62  
63          public static LanguageSet from(final Set<String> langs) {
64              return langs.isEmpty() ? NO_LANGUAGES : new SomeLanguages(langs);
65          }
66  
67          public abstract boolean contains(String language);
68  
69          public abstract String getAny();
70  
71          public abstract boolean isEmpty();
72  
73          public abstract boolean isSingleton();
74  
75          public abstract LanguageSet restrictTo(LanguageSet other);
76      }
77  
78      /**
79       * Some languages, explicitly enumerated.
80       */
81      public static final class SomeLanguages extends LanguageSet {
82          private final Set<String> languages;
83  
84          private SomeLanguages(final Set<String> languages) {
85              this.languages = Collections.unmodifiableSet(languages);
86          }
87  
88          @Override
89          public boolean contains(final String language) {
90              return this.languages.contains(language);
91          }
92  
93          @Override
94          public String getAny() {
95              return this.languages.iterator().next();
96          }
97  
98          public Set<String> getLanguages() {
99              return this.languages;
100         }
101 
102         @Override
103         public boolean isEmpty() {
104             return this.languages.isEmpty();
105         }
106 
107         @Override
108         public boolean isSingleton() {
109             return this.languages.size() == 1;
110         }
111 
112         @Override
113         public LanguageSet restrictTo(final LanguageSet other) {
114             if (other == NO_LANGUAGES) {
115                 return other;
116             } else if (other == ANY_LANGUAGE) {
117                 return this;
118             } else {
119                 final SomeLanguages sl = (SomeLanguages) other;
120                 if (sl.languages.containsAll(languages)) {
121                     return this;
122                 } else {
123                     final Set<String> ls = new HashSet<String>(this.languages);
124                     ls.retainAll(sl.languages);
125                     return from(ls);
126                 }
127             }
128         }
129 
130         @Override
131         public String toString() {
132             return "Languages(" + languages.toString() + ")";
133         }
134 
135     }
136 
137     public static final String ANY = "any";
138 
139     private static final Map<NameType, Languages> LANGUAGES = new EnumMap<NameType, Languages>(NameType.class);
140 
141     static {
142         for (final NameType s : NameType.values()) {
143             LANGUAGES.put(s, getInstance(langResourceName(s)));
144         }
145     }
146 
147     public static Languages getInstance(final NameType nameType) {
148         return LANGUAGES.get(nameType);
149     }
150 
151     public static Languages getInstance(final String languagesResourceName) {
152         // read languages list
153         final Set<String> ls = new HashSet<String>();
154         final InputStream langIS = Languages.class.getClassLoader().getResourceAsStream(languagesResourceName);
155 
156         if (langIS == null) {
157             throw new IllegalArgumentException("Unable to resolve required resource: " + languagesResourceName);
158         }
159 
160         final Scanner lsScanner = new Scanner(langIS, ResourceConstants.ENCODING);
161         boolean inExtendedComment = false;
162         while (lsScanner.hasNextLine()) {
163             final String line = lsScanner.nextLine().trim();
164             if (inExtendedComment) {
165                 if (line.endsWith(ResourceConstants.EXT_CMT_END)) {
166                     inExtendedComment = false;
167                 }
168             } else {
169                 if (line.startsWith(ResourceConstants.EXT_CMT_START)) {
170                     inExtendedComment = true;
171                 } else if (line.length() > 0) {
172                     ls.add(line);
173                 }
174             }
175         }
176 
177         return new Languages(Collections.unmodifiableSet(ls));
178     }
179 
180     private static String langResourceName(final NameType nameType) {
181         return String.format("org/apache/commons/codec/language/bm/%s_languages.txt", nameType.getName());
182     }
183 
184     private final Set<String> languages;
185 
186     /**
187      * No languages at all.
188      */
189     public static final LanguageSet NO_LANGUAGES = new LanguageSet() {
190         @Override
191         public boolean contains(final String language) {
192             return false;
193         }
194 
195         @Override
196         public String getAny() {
197             throw new NoSuchElementException("Can't fetch any language from the empty language set.");
198         }
199 
200         @Override
201         public boolean isEmpty() {
202             return true;
203         }
204 
205         @Override
206         public boolean isSingleton() {
207             return false;
208         }
209 
210         @Override
211         public LanguageSet restrictTo(final LanguageSet other) {
212             return this;
213         }
214 
215         @Override
216         public String toString() {
217             return "NO_LANGUAGES";
218         }
219     };
220 
221     /**
222      * Any/all languages.
223      */
224     public static final LanguageSet ANY_LANGUAGE = new LanguageSet() {
225         @Override
226         public boolean contains(final String language) {
227             return true;
228         }
229 
230         @Override
231         public String getAny() {
232             throw new NoSuchElementException("Can't fetch any language from the any language set.");
233         }
234 
235         @Override
236         public boolean isEmpty() {
237             return false;
238         }
239 
240         @Override
241         public boolean isSingleton() {
242             return false;
243         }
244 
245         @Override
246         public LanguageSet restrictTo(final LanguageSet other) {
247             return other;
248         }
249 
250         @Override
251         public String toString() {
252             return "ANY_LANGUAGE";
253         }
254     };
255 
256     private Languages(final Set<String> languages) {
257         this.languages = languages;
258     }
259 
260     public Set<String> getLanguages() {
261         return this.languages;
262     }
263 }