Languages.java

  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. package org.apache.commons.codec.language.bm;

  18. import java.io.InputStream;
  19. import java.util.Collections;
  20. import java.util.EnumMap;
  21. import java.util.HashSet;
  22. import java.util.Map;
  23. import java.util.NoSuchElementException;
  24. import java.util.Scanner;
  25. import java.util.Set;

  26. /**
  27.  * Language codes.
  28.  * <p>
  29.  * Language codes are typically loaded from resource files. These are UTF-8 encoded text files. They are
  30.  * systematically named following the pattern:
  31.  * <blockquote>org/apache/commons/codec/language/bm/${{@link NameType#getName()} languages.txt</blockquote>
  32.  * <p>
  33.  * The format of these resources is the following:
  34.  * <ul>
  35.  * <li><b>Language:</b> a single string containing no whitespace</li>
  36.  * <li><b>End-of-line comments:</b> Any occurrence of '//' will cause all text following on that line to be
  37.  * discarded as a comment.</li>
  38.  * <li><b>Multi-line comments:</b> Any line starting with '/*' will start multi-line commenting mode.
  39.  * This will skip all content until a line ending in '*' and '/' is found.</li>
  40.  * <li><b>Blank lines:</b> All blank lines will be skipped.</li>
  41.  * </ul>
  42.  * <p>
  43.  * Ported from language.php
  44.  * <p>
  45.  * This class is immutable and thread-safe.
  46.  *
  47.  * @since 1.6
  48.  * @version $Id: Languages.java 1694610 2015-08-07 03:47:38Z ggregory $
  49.  */
  50. public class Languages {
  51.     // Implementation note: This class is divided into two sections. The first part is a static factory interface that
  52.     // exposes org/apache/commons/codec/language/bm/%s_languages.txt for %s in NameType.* as a list of supported
  53.     // languages, and a second part that provides instance methods for accessing this set for supported languages.

  54.     /**
  55.      * A set of languages.
  56.      */
  57.     public static abstract class LanguageSet {

  58.         public static LanguageSet from(final Set<String> langs) {
  59.             return langs.isEmpty() ? NO_LANGUAGES : new SomeLanguages(langs);
  60.         }

  61.         public abstract boolean contains(String language);

  62.         public abstract String getAny();

  63.         public abstract boolean isEmpty();

  64.         public abstract boolean isSingleton();

  65.         public abstract LanguageSet restrictTo(LanguageSet other);

  66.         abstract LanguageSet merge(LanguageSet other);
  67.     }

  68.     /**
  69.      * Some languages, explicitly enumerated.
  70.      */
  71.     public static final class SomeLanguages extends LanguageSet {
  72.         private final Set<String> languages;

  73.         private SomeLanguages(final Set<String> languages) {
  74.             this.languages = Collections.unmodifiableSet(languages);
  75.         }

  76.         @Override
  77.         public boolean contains(final String language) {
  78.             return this.languages.contains(language);
  79.         }

  80.         @Override
  81.         public String getAny() {
  82.             return this.languages.iterator().next();
  83.         }

  84.         public Set<String> getLanguages() {
  85.             return this.languages;
  86.         }

  87.         @Override
  88.         public boolean isEmpty() {
  89.             return this.languages.isEmpty();
  90.         }

  91.         @Override
  92.         public boolean isSingleton() {
  93.             return this.languages.size() == 1;
  94.         }

  95.         @Override
  96.         public LanguageSet restrictTo(final LanguageSet other) {
  97.             if (other == NO_LANGUAGES) {
  98.                 return other;
  99.             } else if (other == ANY_LANGUAGE) {
  100.                 return this;
  101.             } else {
  102.                 final SomeLanguages sl = (SomeLanguages) other;
  103.                 final Set<String> ls = new HashSet<String>(Math.min(languages.size(), sl.languages.size()));
  104.                 for (final String lang : languages) {
  105.                     if (sl.languages.contains(lang)) {
  106.                         ls.add(lang);
  107.                     }
  108.                 }
  109.                 return from(ls);
  110.             }
  111.         }

  112.         @Override
  113.         public LanguageSet merge(final LanguageSet other) {
  114.             if (other == NO_LANGUAGES) {
  115.                 return this;
  116.             } else if (other == ANY_LANGUAGE) {
  117.                 return other;
  118.             } else {
  119.                 final SomeLanguages sl = (SomeLanguages) other;
  120.                 final Set<String> ls = new HashSet<String>(languages);
  121.                 for (final String lang : sl.languages) {
  122.                   ls.add(lang);
  123.                 }
  124.                 return from(ls);
  125.             }
  126.         }

  127.         @Override
  128.         public String toString() {
  129.             return "Languages(" + languages.toString() + ")";
  130.         }

  131.     }

  132.     public static final String ANY = "any";

  133.     private static final Map<NameType, Languages> LANGUAGES = new EnumMap<NameType, Languages>(NameType.class);

  134.     static {
  135.         for (final NameType s : NameType.values()) {
  136.             LANGUAGES.put(s, getInstance(langResourceName(s)));
  137.         }
  138.     }

  139.     public static Languages getInstance(final NameType nameType) {
  140.         return LANGUAGES.get(nameType);
  141.     }

  142.     public static Languages getInstance(final String languagesResourceName) {
  143.         // read languages list
  144.         final Set<String> ls = new HashSet<String>();
  145.         final InputStream langIS = Languages.class.getClassLoader().getResourceAsStream(languagesResourceName);

  146.         if (langIS == null) {
  147.             throw new IllegalArgumentException("Unable to resolve required resource: " + languagesResourceName);
  148.         }

  149.         final Scanner lsScanner = new Scanner(langIS, ResourceConstants.ENCODING);
  150.         try {
  151.             boolean inExtendedComment = false;
  152.             while (lsScanner.hasNextLine()) {
  153.                 final String line = lsScanner.nextLine().trim();
  154.                 if (inExtendedComment) {
  155.                     if (line.endsWith(ResourceConstants.EXT_CMT_END)) {
  156.                         inExtendedComment = false;
  157.                     }
  158.                 } else {
  159.                     if (line.startsWith(ResourceConstants.EXT_CMT_START)) {
  160.                         inExtendedComment = true;
  161.                     } else if (line.length() > 0) {
  162.                         ls.add(line);
  163.                     }
  164.                 }
  165.             }
  166.         } finally {
  167.             lsScanner.close();
  168.         }

  169.         return new Languages(Collections.unmodifiableSet(ls));
  170.     }

  171.     private static String langResourceName(final NameType nameType) {
  172.         return String.format("org/apache/commons/codec/language/bm/%s_languages.txt", nameType.getName());
  173.     }

  174.     private final Set<String> languages;

  175.     /**
  176.      * No languages at all.
  177.      */
  178.     public static final LanguageSet NO_LANGUAGES = new LanguageSet() {
  179.         @Override
  180.         public boolean contains(final String language) {
  181.             return false;
  182.         }

  183.         @Override
  184.         public String getAny() {
  185.             throw new NoSuchElementException("Can't fetch any language from the empty language set.");
  186.         }

  187.         @Override
  188.         public boolean isEmpty() {
  189.             return true;
  190.         }

  191.         @Override
  192.         public boolean isSingleton() {
  193.             return false;
  194.         }

  195.         @Override
  196.         public LanguageSet restrictTo(final LanguageSet other) {
  197.             return this;
  198.         }

  199.         @Override
  200.         public LanguageSet merge(final LanguageSet other) {
  201.             return other;
  202.         }

  203.         @Override
  204.         public String toString() {
  205.             return "NO_LANGUAGES";
  206.         }
  207.     };

  208.     /**
  209.      * Any/all languages.
  210.      */
  211.     public static final LanguageSet ANY_LANGUAGE = new LanguageSet() {
  212.         @Override
  213.         public boolean contains(final String language) {
  214.             return true;
  215.         }

  216.         @Override
  217.         public String getAny() {
  218.             throw new NoSuchElementException("Can't fetch any language from the any language set.");
  219.         }

  220.         @Override
  221.         public boolean isEmpty() {
  222.             return false;
  223.         }

  224.         @Override
  225.         public boolean isSingleton() {
  226.             return false;
  227.         }

  228.         @Override
  229.         public LanguageSet restrictTo(final LanguageSet other) {
  230.             return other;
  231.         }

  232.         @Override
  233.         public LanguageSet merge(final LanguageSet other) {
  234.             return other;
  235.         }

  236.         @Override
  237.         public String toString() {
  238.             return "ANY_LANGUAGE";
  239.         }
  240.     };

  241.     private Languages(final Set<String> languages) {
  242.         this.languages = languages;
  243.     }

  244.     public Set<String> getLanguages() {
  245.         return this.languages;
  246.     }
  247. }