001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.codec.language.bm; 019 020 import java.io.InputStream; 021 import java.util.Collections; 022 import java.util.EnumMap; 023 import java.util.HashSet; 024 import java.util.Map; 025 import java.util.NoSuchElementException; 026 import java.util.Scanner; 027 import java.util.Set; 028 029 /** 030 * Language codes. 031 * <p> 032 * Language codes are typically loaded from resource files. These are UTF-8 encoded text files. They are 033 * systematically named following the pattern: 034 * <blockquote>org/apache/commons/codec/language/bm/${{@link NameType#getName()} languages.txt</blockquote> 035 * <p> 036 * The format of these resources is the following: 037 * <ul> 038 * <li><b>Language:</b> a single string containing no whitespace</li> 039 * <li><b>End-of-line comments:</b> Any occurrence of '//' will cause all text following on that line to be 040 * discarded as a comment.</li> 041 * <li><b>Multi-line comments:</b> Any line starting with '/*' will start multi-line commenting mode. 042 * This will skip all content until a line ending in '*' and '/' is found.</li> 043 * <li><b>Blank lines:</b> All blank lines will be skipped.</li> 044 * </ul> 045 * <p> 046 * Ported from language.php 047 * <p> 048 * This class is immutable and thread-safe. 049 * 050 * @since 1.6 051 * @version $Id: Languages.LanguageSet.html 889935 2013-12-11 05:05:13Z ggregory $ 052 */ 053 public class Languages { 054 // Iimplementation note: This class is divided into two sections. The first part is a static factory interface that 055 // exposes org/apache/commons/codec/language/bm/%s_languages.txt for %s in NameType.* as a list of supported 056 // languages, and a second part that provides instance methods for accessing this set fo supported languages. 057 058 /** 059 * A set of languages. 060 */ 061 public static abstract class LanguageSet { 062 063 public static LanguageSet from(Set<String> langs) { 064 return langs.isEmpty() ? NO_LANGUAGES : new SomeLanguages(langs); 065 } 066 067 public abstract boolean contains(String language); 068 069 public abstract String getAny(); 070 071 public abstract boolean isEmpty(); 072 073 public abstract boolean isSingleton(); 074 075 public abstract LanguageSet restrictTo(LanguageSet other); 076 } 077 078 /** 079 * Some languages, explicitly enumerated. 080 */ 081 public static final class SomeLanguages extends LanguageSet { 082 private final Set<String> languages; 083 084 private SomeLanguages(Set<String> languages) { 085 this.languages = Collections.unmodifiableSet(languages); 086 } 087 088 @Override 089 public boolean contains(String language) { 090 return this.languages.contains(language); 091 } 092 093 @Override 094 public String getAny() { 095 return this.languages.iterator().next(); 096 } 097 098 public Set<String> getLanguages() { 099 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(LanguageSet other) { 114 if (other == NO_LANGUAGES) { 115 return other; 116 } else if (other == ANY_LANGUAGE) { 117 return this; 118 } else { 119 SomeLanguages sl = (SomeLanguages) other; 120 if (sl.languages.containsAll(languages)) { 121 return this; 122 } else { 123 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 (NameType s : NameType.values()) { 143 LANGUAGES.put(s, getInstance(langResourceName(s))); 144 } 145 } 146 147 public static Languages getInstance(NameType nameType) { 148 return LANGUAGES.get(nameType); 149 } 150 151 public static Languages getInstance(String languagesResourceName) { 152 // read languages list 153 Set<String> ls = new HashSet<String>(); 154 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 Scanner lsScanner = new Scanner(langIS, ResourceConstants.ENCODING); 161 boolean inExtendedComment = false; 162 while (lsScanner.hasNextLine()) { 163 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(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(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(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(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(LanguageSet other) { 247 return other; 248 } 249 250 @Override 251 public String toString() { 252 return "ANY_LANGUAGE"; 253 } 254 }; 255 256 private Languages(Set<String> languages) { 257 this.languages = languages; 258 } 259 260 public Set<String> getLanguages() { 261 return this.languages; 262 } 263 }