<?PHP
#
#   FILE:  Vocabulary.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2007-2013 Edward Almasy and Internet Scout Research Group
#   http://scout.wisc.edu/cwis/
#

/**
* Controlled vocabulary.
*/
class Vocabulary {

    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Object constructor.
    * @param string $FileName Name of .voc file containing vocabulary to load.
    * @note Check Status() to determine if constructor succeeded
    */
    public function __construct($FileName)
    {
        # save file name
        $this->FileName = $FileName;

        # attempt to load vocabulary from file
        $this->Xml = simplexml_load_file($FileName);

        # set error code if load failed
        $this->StatusString = ($this->Xml === FALSE) ? "XML Load Failed" : "OK";
        $this->Xml = isset($this->Xml->vocabulary) ? $this->Xml->vocabulary : $this->Xml;
    }

    /**
    * Get string indicate status of last action.
    */
    public function Status()
    {
        return $this->StatusString;
    }

    /**
    * Get hash string for vocabulary (generated from file name).
    * @return string 32-character hash string.
    */
    public function Hash()
    {
        return self::HashForFile($this->FileName);
    }

    /**
    * Get hash string for specified vocabulary file name.
    * @param string $FileName Name of .voc file containing vocabulary.
    * @return string 32-character hash string.
    */
    static function HashForFile($FileName = NULL)
    {
        return strtoupper(md5($FileName));
    }

    /**
    * Get vocabulary name.
    * @return string Vocabulary name.
    */
    public function Name()
    {
        return $this->XmlVal("name");
    }

    /**
    * Get vocabulary description.
    * @return string Vocabulary description.
    */
    public function Description()
    {
        return $this->XmlVal("description");
    }

    /**
    * Get URL attached to vocabulary.
    * @return string URL associated with vocabulary.
    */
    public function Url()
    {
        return $this->XmlVal("url");
    }

    /**
    * Get version number for vocabulary.
    * @return string Vocabulary version.
    */
    public function Version()
    {
        return $this->XmlVal("version");
    }

    /**
    * Get whether vocabulary has associated qualifier.
    * @return bool TRUE if vocabulary has qualifier, otherwise FALSE.
    */
    public function HasQualifier()
    {
        return (strlen($this->QualifierName())
                && (strlen($this->QualifierNamespace())
                        || strlen($this->QualifierUrl()))) ? TRUE : FALSE;
    }

    /**
    * Get qualifier name.
    * @return string Qualifier name, or empty string if no qualifier name
    *       available or no qualifier associated with vocabulary.
    */
    public function QualifierName()
    {
        return isset($this->Xml->qualifier->name)
                ? (string)$this->Xml->qualifier->name : "";
    }

    /**
    * Get qualifier namespace.
    * @return string Qualifier namespace, or empty string if no qualifier
    *       namespace available or no qualifier associated with vocabulary.
    */
    public function QualifierNamespace()
    {
        return isset($this->Xml->qualifier->namespace)
                ? (string)$this->Xml->qualifier->namespace : "";
    }

    /**
    * Get qualifier URL.
    * @return string Qualifier URL, or empty string if no qualifier
    *       URL available or no qualifier associated with vocabulary.
    */
    public function QualifierUrl()
    {
        return isset($this->Xml->qualifier->url)
                ? (string)$this->Xml->qualifier->url : "";
    }

    /**
    * Get name of owning (maintaining) organization.
    * @return string Name of owner or empty string if no owner name available.
    */
    public function OwnerName()
    {
        return isset($this->Xml->owner->name)
                ? (string)$this->Xml->owner->name : "";
    }

    /**
    * Get primary URL for owning (maintaining) organization.
    * @return string URL for owner or empty string if no owner URL available.
    */
    public function OwnerUrl()
    {
        return isset($this->Xml->owner->url)
                ? (string)$this->Xml->owner->url : "";
    }

    /**
    * Get vocabulary terms as multi-dimensional array.
    * @return array Associative hierarchical array with terms for index.
    */
    public function TermArray()
    {
        $Terms = $this->ExtractTermSet($this->Xml);

        # return array of terms to caller
        return $Terms;
    }

    /**
    * Get vocabulary terms as flat array with double-dash separators.
    * @return array Array of terms.
    */
    public function TermList()
    {
        $TermTree = $this->TermArray();
        $Terms = $this->BuildTermList("", $TermTree);
        return $Terms;
    }

    # ---- PRIVATE INTERFACE -------------------------------------------------

    private $FileName;
    private $StatusString;
    private $Xml;

    /**
    * Get value from stored parsed XML.
    * @return string Retrieved value.
    */
    private function XmlVal($ValueName)
    {
        return isset($this->Xml->{$ValueName})
                ? (string)$this->Xml->{$ValueName} : "";
    }

    /**
    * Get terms from parsed XML as multi-dimensional array.
    * @param object Parsed XML as SimpleXMLElement object.
    * @return array Associative hierarchical array with terms for index.
    */
    private function ExtractTermSet($Tree)
    {
        # make sure a valid SimpleXMLElement was given and return an empty
        # array if not
        if (!($Tree instanceof SimpleXMLElement))
        {
            return array();
        }

        $Terms = array();
        foreach ($Tree->term as $Term)
        {
            if (isset($Term->value))
            {
                $Terms[(string)$Term->value] = $this->ExtractTermSet($Term);
            }
            else
            {
                $Terms[(string)$Term] = array();
            }
        }
        return $Terms;
    }

    /**
    * Build double-dash separated term list from hierarchical array.
    * @param string $Prefix Prefix for current point in hierarchical array.
    * @param array $TermTree Hierarchical array.
    * @return array Term list.
    */
    private function BuildTermList($Prefix, $TermTree)
    {
        $Terms = array();
        foreach ($TermTree as $Term => $Children)
        {
            $Term = trim($Term);
            $NewTerm = strlen($Prefix) ? $Prefix." -- ".$Term : $Term;
            $Terms[] = $NewTerm;
            $Terms = array_merge($Terms, $this->BuildTermList($NewTerm, $Children));
        }
        return $Terms;
    }
}
