001/* 002 * lang-tag 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.langtag; 019 020 021import java.util.*; 022 023 024/** 025 * Language tag utilities. 026 */ 027public final class LangTagUtils { 028 029 030 /** 031 * Strips the language tag, if any is found, from the specified string. 032 * This method is {@code null} safe. 033 * 034 * <p>Example: 035 * 036 * <pre> 037 * "name#bg-BG" => "name" 038 * "name" => "name" 039 * </pre> 040 * 041 * @param s The string. May contain a language tag. May be 042 * {@code null}. 043 * 044 * @return The string with no language tag, or {@code null} if the 045 * original string is {@code null}. 046 */ 047 public static String strip(final String s) { 048 049 if (s == null) 050 return null; 051 052 final int pos = s.indexOf('#'); 053 054 if (pos < 0) 055 return s; 056 057 return s.substring(0, pos); 058 } 059 060 061 /** 062 * Strips the language tags, if any are found, from the specified 063 * string set. This method is {@code null} safe. 064 * 065 * <p>Example: 066 * 067 * <pre> 068 * "name#bg-BG" => "name" 069 * "name" => "name" 070 * </pre> 071 * 072 * @param set The string set. May contain strings with language tags. 073 * May be {@code null}. 074 * 075 * @return The string set with no language tags, or {@code null} if the 076 * original set is {@code null}. 077 */ 078 public static Set<String> strip(final Set<String> set) { 079 080 if (set == null) 081 return null; 082 083 Set<String> out = new HashSet<String>(); 084 085 for (String s: set) 086 out.add(strip(s)); 087 088 return out; 089 } 090 091 092 /** 093 * Strips the language tags, if any are found, from the specified 094 * string list. This method is {@code null} safe. 095 * 096 * <p>Example: 097 * 098 * <pre> 099 * "name#bg-BG" => "name" 100 * "name" => "name" 101 * </pre> 102 * 103 * @param list The string list. May contain strings with language tags. 104 * May be {@code null}. 105 * 106 * @return The string list with no language tags, or {@code null} if 107 * the original list is {@code null}. 108 */ 109 public static List<String> strip(final List<String> list) { 110 111 if (list == null) 112 return null; 113 114 List<String> out = new ArrayList<String>(list.size()); 115 116 for (String s: list) 117 out.add(strip(s)); 118 119 return out; 120 } 121 122 123 /** 124 * Extracts the language tag, if any is found, from the specified 125 * string. 126 * 127 * <p>Example: 128 * 129 * <pre> 130 * "name#bg-BG" => "bg-BG" 131 * "name#" => null 132 * "name" => null 133 * </pre> 134 * 135 * @param s The string. May contain a language tag. May be 136 * {@code null}. 137 * 138 * @return The extracted language tag, {@code null} if not found. 139 * 140 * @throws LangTagException If the language tag is invalid. 141 */ 142 public static LangTag extract(final String s) 143 throws LangTagException { 144 145 if (s == null) 146 return null; 147 148 final int pos = s.indexOf('#'); 149 150 if (pos < 0 || s.length() < pos + 1) 151 return null; 152 153 return LangTag.parse(s.substring(pos + 1)); 154 } 155 156 157 /** 158 * Finds all language-tagged entries with the specified base name. 159 * Entries with invalid language tags will be skipped. 160 * 161 * <p>Example: 162 * 163 * <p>Map to search for base name "month": 164 * 165 * <pre> 166 * "month" => "January" 167 * "month#de" => "Januar" 168 * "month#fr" => "janvier" 169 * "month#pt" => "janeiro" 170 * </pre> 171 * 172 * <p>Result: 173 * 174 * <pre> 175 * null => "January" 176 * "de" => "Januar" 177 * "fr" => "janvier" 178 * "pt" => "janeiro" 179 * </pre> 180 * 181 * @param baseName The base name to look for (without a language tag) 182 * in the map keys. Must not be {@code null}. 183 * @param map The map to search. Must not be {@code null}. 184 * 185 * @return A map of all language-tagged entries with the specified 186 * base name. A {@code null} keyed entry will indicate no 187 * language tag (base name only). 188 */ 189 public static <T> Map<LangTag,T> find(final String baseName, final Map<String,T> map) { 190 191 Map<LangTag,T> result = new HashMap<LangTag,T>(); 192 193 // Walk through each map entry, checking for entry keys that 194 // start with "baseName" 195 for (Map.Entry<String,T> entry: map.entrySet()) { 196 197 T value; 198 199 try { 200 value = entry.getValue(); 201 202 } catch (ClassCastException e) { 203 204 continue; // skip 205 } 206 207 if (entry.getKey().equals(baseName)) { 208 209 // Claim name matches, no tag 210 result.put(null, value); 211 } 212 else if (entry.getKey().startsWith(baseName + '#')) { 213 214 // Claim name matches, has tag 215 String[] parts = entry.getKey().split("#", 2); 216 217 LangTag langTag = null; 218 219 if (parts.length == 2) { 220 221 try { 222 langTag = LangTag.parse(parts[1]); 223 224 } catch (LangTagException e) { 225 226 // ignore 227 } 228 } 229 230 result.put(langTag, value); 231 } 232 } 233 234 return result; 235 } 236 237 238 /** 239 * Returns a string list representation of the specified language tags 240 * collection. 241 * 242 * @param langTags The language tags list. May be {@code null}. 243 * 244 * @return The string list, or {@code null} if the original list is 245 * {@code null}. 246 */ 247 public static List<String> toStringList(final Collection<LangTag> langTags) { 248 249 if (langTags == null) 250 return null; 251 252 List<String> out = new ArrayList<String>(langTags.size()); 253 254 for (LangTag lt: langTags) { 255 out.add(lt.toString()); 256 } 257 258 return out; 259 } 260 261 262 /** 263 * Returns a string array representation of the specified language tags 264 * collection. 265 * 266 * @param langTags The language tags list. May be {@code null}. 267 * 268 * @return The string list, or {@code null} if the original list is 269 * {@code null}. 270 */ 271 public static String[] toStringArray(final Collection<LangTag> langTags) { 272 273 if (langTags == null) 274 return null; 275 276 String[] out = new String[langTags.size()]; 277 278 int i=0; 279 280 for (LangTag lt: langTags) { 281 out[i++] = lt.toString(); 282 } 283 284 return out; 285 } 286 287 288 /** 289 * Parses a language tag list from the specified string containing 290 * space delimited values. 291 * 292 * @param spaceDelimitedValues String containing zero or more space 293 * delimited values. May be {@code null}. 294 * 295 * @return The language tag list, or {@code null} if the parsed string 296 * is {@code null}. 297 * 298 * @throws LangTagException If parsing failed. 299 */ 300 public static List<LangTag> parseLangTagList(final String spaceDelimitedValues) 301 throws LangTagException { 302 303 if (spaceDelimitedValues == null) { 304 return null; 305 } 306 307 List<LangTag> out = new LinkedList<LangTag>(); 308 309 StringTokenizer st = new StringTokenizer(spaceDelimitedValues, " "); 310 311 while (st.hasMoreTokens()) { 312 out.add(LangTag.parse(st.nextToken())); 313 } 314 315 return out; 316 } 317 318 319 /** 320 * Parses a language tag list from the specified string collection. 321 * 322 * @param collection The string collection. May be {@code null}. 323 * 324 * @return The language tag list, or {@code null} if the parsed string 325 * collection is {@code null}. 326 * 327 * @throws LangTagException If parsing failed. 328 */ 329 public static List<LangTag> parseLangTagList(final Collection<String> collection) 330 throws LangTagException { 331 332 if (collection == null) 333 return null; 334 335 List<LangTag> out = new ArrayList<LangTag>(collection.size()); 336 337 for (String s: collection) { 338 out.add(LangTag.parse(s)); 339 } 340 341 return out; 342 } 343 344 345 /** 346 * Parses a language tag list from the specified string values. 347 * 348 * @param values The string values. May be {@code null}. 349 * 350 * @return The language tag list, or {@code null} if the parsed string 351 * array is {@code null}. 352 * 353 * @throws LangTagException If parsing failed. 354 */ 355 public static List<LangTag> parseLangTagList(final String ... values) 356 throws LangTagException { 357 358 if (values == null) 359 return null; 360 361 List<LangTag> out = new ArrayList<LangTag>(values.length); 362 363 for (String s: values) { 364 out.add(LangTag.parse(s)); 365 } 366 367 return out; 368 } 369 370 371 /** 372 * Parses a language tag array from the specified string values. 373 * 374 * @param values The string values. May be {@code null}. 375 * 376 * @return The language tag array, or {@code null} if the parsed string 377 * array is {@code null}. 378 * 379 * @throws LangTagException If parsing failed. 380 */ 381 public static LangTag[] parseLangTagArray(final String ... values) 382 throws LangTagException { 383 384 if (values == null) 385 return null; 386 387 LangTag[] out = new LangTag[values.length]; 388 389 for (int i=0; i < values.length; i++) { 390 out[i] = LangTag.parse(values[i]); 391 } 392 393 return out; 394 } 395 396 397 /** 398 * Splits the specified optionally language tagged string into a string 399 * and language tag pair. 400 * 401 * @param s The optionally language-tagged string. May be {@code null}. 402 * 403 * @return The pair, with {@code null} language tag if none found. 404 * {@code null} if the original value is {@code null}. 405 * 406 * @throws LangTagException If parsing failed. 407 */ 408 public static Map.Entry<String,LangTag> split(final String s) 409 throws LangTagException { 410 411 if (s == null) 412 return null; 413 414 if ("#".equals(s)) 415 return new AbstractMap.SimpleImmutableEntry<String, LangTag>("#", null); 416 417 final int pos = s.indexOf('#'); 418 419 if (pos < 0 || s.length() < pos + 1) 420 return new AbstractMap.SimpleImmutableEntry<String, LangTag>(s, null); 421 422 return new AbstractMap.SimpleImmutableEntry<String, LangTag>( 423 s.substring(0, pos), 424 LangTag.parse(s.substring(pos + 1))); 425 } 426 427 428 /** 429 * Concatenates the specified language tags with a single space. 430 * 431 * @param langTags The language tags list. May be {@code null}. 432 * 433 * @return The string, {@code null} if the language tag list was 434 * {@code null} or empty. 435 */ 436 public static String concat(final List<LangTag> langTags) { 437 438 if (langTags == null || langTags.isEmpty()) { 439 return null; 440 } 441 442 StringBuilder sb = new StringBuilder(); 443 444 for (LangTag tag: langTags) { 445 446 if (tag == null) { 447 continue; 448 } 449 450 if (sb.length() > 0) 451 sb.append(' '); 452 453 sb.append(tag); 454 } 455 456 return sb.toString(); 457 } 458 459 460 /** 461 * Prevents public instantiation. 462 */ 463 private LangTagUtils() { } 464}