content: /**
   * Copyright 2000-2006 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts.modules.phonemiser;
  
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  
  import mf.org.w3c.dom.Element;
  
  
  public class Allophone {
      private final Map<String, String> features;
      private String name;
  
      /**
       * Create a new Allophone object from the given XML Element
       *
       * @param a            the allophone definition element
       * @param featureNames the names of features to use for defining the allophone
       * @throws IllegalArgumentException if a is not as expected
       */
      public Allophone(Element a, String[] featureNames) {
          name = a.getAttribute("ph");
          String vc;
          String isTone;
          if (name.equals(""))
              throw new IllegalArgumentException("Element must have a 'ph' attribute");
          if (a.getTagName().equals("consonant")) {
              vc = "-";
              isTone = "-";
          } else if (a.getTagName().equals("vowel")) {
              vc = "+";
              isTone = "-";
          } else if (a.getTagName().equals("silence")) {
              vc = "0";
              isTone = "-";
          } else if (a.getTagName().equals("tone")) {
              vc = "0";
              isTone = "+";
          } else {
              throw new IllegalArgumentException("Element must be one of <vowel>, <consonant> and <silence>, but is <" + a.getTagName() + ">");
          }
          Map<String, String> feats = new HashMap<String, String>();
          feats.put("vc", vc);
          feats.put("isTone", isTone);
          for (String f : featureNames) {
              feats.put(f, getAttribute(a, f));
          }
          this.features = Collections.unmodifiableMap(feats);
      }
  
      /**
       * Return the requested attribute of e, or "0" if there is no such attribute.
       *
       * @param e
       * @param att
       * @return
       */
      private String getAttribute(Element e, String att) {
          String val = e.getAttribute(att);
          if (val.equals("")) return "0";
          return val;
      }
  
      public String name() {
          return name;
      }
  
      @Override
      public String toString() {
          return name;
      }
  
      public boolean isVowel() {
          return "+".equals(features.get("vc"));
      }
  
      public boolean isSyllabic() {
          return isVowel();
      }
  
      public boolean isConsonant() {
          return "-".equals(features.get("vc"));
      }
  
      public boolean isVoiced() {
          return isVowel() || "+".equals(features.get("cvox"));
      }
  
      public boolean isSonorant() {
          return "lnr".contains(features.get("ctype"));
      }
  
      public boolean isLiquid() {
          return "l".equals(features.get("ctype"));
      }
  
      public boolean isNasal() {
          return "n".equals(features.get("ctype"));
      }
  
      public boolean isGlide() {
          return "r".equals(features.get("ctype")) && !isVowel();
      }
  
      public boolean isFricative() {
          return "f".equals(features.get("ctype"));
      }
  
      public boolean isPlosive() {
          return "s".equals(features.get("ctype"));
      }
  
      public boolean isAffricate() {
          return "a".equals(features.get("ctype"));
      }
  
      public boolean isPause() {
          return "0".equals(features.get("vc")) &&
                  "-".equals(features.get("isTone"));
      }
  
      /**
       * Whether the Allophone object represents a tone symbol.
       *
       * @return
       */
      public boolean isTone() {
          return "+".equals(features.get("isTone"));
      }
  
      public int sonority() {
          if (isVowel()) {
              String vlng = features.get("vlng");
              if (vlng == null)
                  return 5; // language doesn't make a distinction between vowels of different length
              if ("ld".contains(vlng)) return 6;
              else if ("s".equals(vlng)) return 5;
              else if ("a".equals(vlng)) return 4;
              else return 5; // unknown vowel length
          } else if (isSonorant()) return 3;
          else if (isFricative()) return 2;
          return 1;
      }
  
      /**
       * Get the key-value map of features and feature values for this allophone.
       *
       * @return an unmodifiable map.
       */
      public Map<String, String> getFeatures() {
          return features;
      }
  
      /**
       * Return the feature with name feat.
       * Three types of values are possible: 1. an informative feature; 2. the value "0" to indicate
       * that the feature exists but is not meaningful for this allophone; 3. null to indicate that the feature
       * does not exist.
       *
       * @param feat
       * @return the feature value, or null
       */
      public String getFeature(String feat) {
          return features.get(feat);
      }
  }
  
  