Location: PHPKode > projects > VuFind > vufind-1.0.1/web/sys/CitationBuilder.php
<?php
/**
 *
 * Copyright (C) Villanova University 2010.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/**
 * Citation Builder Class
 *
 * This class builds APA and MLA citations.
 *
 * @author      Demian Katz <hide@address.com>
 * @access      public
 */
class CitationBuilder
{
    private $details;
    
    /**
     * Constructor
     *
     * Load the base data needed to build the citations.  The $details parameter
     * should contain as many of the following keys as possible:
     *
     *  authors         => Array of authors in "Last, First, Title, Dates" format.
     *                     i.e. King, Martin Luther, Jr., 1929-1968.
     *  title           => The primary title of the work.
     *  subtitle        => Subtitle of the work.
     *  edition         => Array of edition statements (i.e. "1st ed.").
     *  pubPlace        => Place of publication.
     *  pubName         => Name of publisher.
     *  pubDate         => Year of publication.
     *
     * Unless noted as an array, each field should be a string.
     *
     * @param   array       $details        An array of details used to build
     *                                      the citations.  See above for a full
     *                                      list of keys to populate.
     * @access  public
     */
    public function __construct($details)
    {
        $this->details = $details;
    }
    
    /**
     * Get APA citation.
     *
     * This function assigns all the necessary variables and then returns a template
     * name to display an APA citation.
     *
     * @access  public
     * @return  string                      Path to a Smarty template to display
     *                                      the citation.
     */
    public function getAPA()
    {
        global $interface;
        $apa = array(
            'title' => $this->getAPATitle(),
            'authors' => $this->getAPAAuthors(),
            'publisher' => $this->getPublisher(),
            'year' => $this->getYear(),
            'edition' => $this->getEdition()
        );
        $interface->assign('apaDetails', $apa);
        return 'Citation/apa.tpl';
    }
    
    /**
     * Get MLA citation.
     *
     * This function assigns all the necessary variables and then returns a template
     * name to display an MLA citation.
     *
     * @access  public
     * @return  string                      Path to a Smarty template to display
     *                                      the citation.
     */
    public function getMLA()
    {
        global $interface;
        $mla = array(
            'title' => $this->getMLATitle(),
            'authors' => $this->getMLAAuthors(),
            'publisher' => $this->getPublisher(),
            'year' => $this->getYear(),
            'edition' => $this->getEdition()
        );
        $interface->assign('mlaDetails', $mla);
        return 'Citation/mla.tpl';
    }
    
    /**
     * Is the string a valid name suffix?
     *
     * @access  private
     * @param   string      $str            The string to check.
     * @return  bool                        True if it's a name suffix.
     */
    private function isNameSuffix($str)
    {
        $str = $this->stripPunctuation($str);
        
        // Is it a standard suffix?
        $suffixes = array('Jr', 'Sr');
        if (in_array($str, $suffixes)) {
            return true;
        }
        
        // Is it a roman numeral?  (This check could be smarter, but it's probably
        // good enough as it is).
        if (preg_match('/^[MDCLXVI]+$/', $str)) {
            return true;
        }
        
        // If we got this far, it's not a suffix.
        return false;
    }
    
    /**
     * Is the string a date range?
     *
     * @access  private
     * @param   string      $str            The string to check.
     * @return  bool                        True if it's a date range.
     */
    private function isDateRange($str)
    {
        $str = trim($str);
        return preg_match('/^([0-9]+)-([0-9]*)\.?$/', $str);
    }
    
    /**
     * Abbreviate a first name.
     *
     * @access  private
     * @param   string      $name           The name to abbreviate
     * @return  string                      The abbreviated name.
     */
    private function abbreviateName($name)
    {
        $parts = explode(', ', $name);
        $name = $parts[0];
        
        // Attach initials... but if we encountered a date range, the name
        // ended earlier than expected, and we should stop now.
        if (isset($parts[1]) && !$this->isDateRange($parts[1])) {
            $fnameParts = explode(' ' , $parts[1]);
            for ($i = 0; $i < count($fnameParts); $i++) {
                $fnameParts[$i] = substr($fnameParts[$i], 0, 1) . '.';
            }
            $name .= ', ' . implode(' ', $fnameParts);
            if ($this->isNameSuffix($parts[2])) {
                $name = trim($name) . ', ' . $parts[2];
            }
        }
        
        return trim($name);
    }
    
    /**
     * Strip the dates off the end of a name.
     *
     * @access  private
     * @param   string      $str            Name to reverse.
     * @return  string                      Reversed name.
     */
    private function cleanNameDates($str)
    {
        $arr = explode(', ', $str);
        $name = $arr[0];
        if (isset($arr[1]) && !$this->isDateRange($arr[1])) {
            $name .= ', ' . $arr[1];
            if ($this->isNameSuffix($arr[2])) {
                $name .= ', ' . $arr[2];
            }
        }
        return $name;
    }
    
    /**
     * Strip unwanted punctuation from the right side of a string.
     *
     * @access  private
     * @param   string      $text           Text to clean up.
     * @return  string                      Cleaned up text.
     */
    private function stripPunctuation($text)
    {
        $text = trim($text);
        if ((substr($text, -1) == '.') ||
            (substr($text, -1) == ',') ||
            (substr($text, -1) == ':') ||
            (substr($text, -1) == ';') ||
            (substr($text, -1) == '/')) {
            $text = substr($text, 0, -1);
        }
        return trim($text);
    }
    
    /**
     * Turn a "Last, First" name into a "First Last" name.
     *
     * @access  private
     * @param   string      $str            Name to reverse.
     * @return  string                      Reversed name.
     */
    private function reverseName($str)
    {
        $arr = explode(', ', $str);
        
        // If the second chunk is a date range, there is nothing to reverse!
        if (!isset($arr[1]) || $this->isDateRange($arr[1])) {
            return $arr[0];
        }
        
        $name = $arr[1] . ' ' . $arr[0];
        if ($this->isNameSuffix($arr[2])) {
            $name .= ', ' . $arr[2];
        }
        return $name;
    }
    
    /**
     * Capitalize all words in a title, except for a few common exceptions.
     *
     * @access  private
     * @param   string      $str            Title to capitalize.
     * @return  string                      Capitalized title.
     */
    private function capitalizeTitle($str)
    {
        $exceptions = array('a', 'an', 'the', 'against', 'between', 'in', 'of', 
            'to', 'and', 'but', 'for', 'nor', 'or', 'so', 'yet', 'to');

        $words = split(' ', $str);
        $newwords = array();
        $followsColon = false;
        foreach ($words as $word) {
            // Capitalize words unless they are in the exception list...  but even
            // exceptional words get capitalized if they follow a colon.
            if (!in_array($word, $exceptions) || $followsColon) {
                $word = ucfirst($word);
            }
            array_push($newwords, $word);
            
            $followsColon = substr($word, -1) == ':';
        }
    
        return ucfirst(join(' ', $newwords));
    }

    /**
     * Get the full title for an APA citation.
     *
     * @access  private
     * @return  string
     */
    private function getAPATitle()
    {
        // Create Title
        $title = $this->stripPunctuation($this->details['title']);
        if (isset($this->details['subtitle'])) {
            $title .= ': ' . $this->stripPunctuation($this->details['subtitle']);
        }
        
        // Add period to titles not ending in punctuation
        if (!((substr($title, -1) == '?') || (substr($title, -1) == '!'))) {
            $title .= '.';
        }
        
        return $title;
    }
    
    /**
     * Get an array of authors for an APA citation.
     *
     * @access  private
     * @return  array
     */
    private function getAPAAuthors()
    {
        $authorStr = '';
        if (isset($this->details['authors']) && is_array($this->details['authors'])) {
            $i = 0;
            foreach($this->details['authors'] as $author) {
                $author = $this->abbreviateName($author);
                if (($i+1 == count($this->details['authors'])) && ($i > 0)) { // Last
                    $authorStr .= ', & ' . $this->stripPunctuation($author) . '.';
                } elseif ($i > 0) {
                    $authorStr .= ', ' . $this->stripPunctuation($author) . '.';
                } else {
                    $authorStr .= $this->stripPunctuation($author) . '.';
                }
                $i++;
            }
        }
        return (empty($authorStr) ? false : $authorStr);
    }
    
    /**
     * Get edition statement for inclusion in a citation.  Shared by APA and
     * MLA functionality.
     *
     * @access  private
     * @return  string
     */
    private function getEdition()
    {
        // Find the first edition statement that isn't "1st ed."
        if (isset($this->details['edition']) && 
            is_array($this->details['edition'])) {
            foreach($this->details['edition'] as $edition) {
                if ($edition !== '1st ed.') {
                    return $edition;
                }
            }
        }
        
        // No edition statement found:
        return false;
    }

    /**
     * Get the full title for an MLA citation.
     *
     * @access  private
     * @return  string
     */
    private function getMLATitle()
    {
        // MLA titles are just like APA titles, only capitalized differently:
        return $this->capitalizeTitle($this->getAPATitle());
    }
    
    /**
     * Get an array of authors for an APA citation.
     *
     * @access  private
     * @return  array
     */
    private function getMLAAuthors()
    {
        $authorStr = '';
        if (isset($this->details['authors']) && is_array($this->details['authors'])) {
            $i = 0;
            if (count($this->details['authors']) > 4) {
                $author = $this->details['authors'][0];
                $authorStr = $this->cleanNameDates($author) . ', et al';
            } else {
                foreach($this->details['authors'] as $author) {
                    if (($i+1 == count($this->details['authors'])) && ($i > 0)) { 
                        // Last
                        $authorStr .= ', and ' . 
                            $this->reverseName($this->stripPunctuation($author));
                    } elseif ($i > 0) {
                        $authorStr .= ', ' . 
                            $this->reverseName($this->stripPunctuation($author));
                    } else { 
                        // First
                        $authorStr .= $this->cleanNameDates($author);
                    }
                    $i++;
                }
            }
        }
        return (empty($authorStr) ? false : $this->stripPunctuation($authorStr));
    }
    
    /**
     * Get publisher information (place: name) for inclusion in a citation.
     * Shared by APA and MLA functionality.
     *
     * @access  private
     * @return  string
     */
    private function getPublisher()
    {
        $parts = array();
        if (isset($this->details['pubPlace']) && !empty($this->details['pubPlace'])) {
            $parts[] = $this->stripPunctuation($this->details['pubPlace']);
        }
        if (isset($this->details['pubName']) && !empty($this->details['pubName'])) {
            $parts[] = $this->details['pubName'];
        }
        if (empty($parts)) {
            return false;
        }
        return $this->stripPunctuation(implode(': ', $parts));
    }
    
    /**
     * Get the year of publication for inclusion in a citation.
     * Shared by APA and MLA functionality.
     *
     * @access  private
     * @return  string
     */
    private function getYear()
    {
        if (isset($this->details['pubDate'])) {
            return preg_replace('/[^0-9]/', '', $this->details['pubDate']);
        }
        return false;
    }
}
?>
Return current item: VuFind