add LibRIS code
This commit is contained in:
parent
a3551b20e8
commit
4c95532808
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
namespace LibRIS;
|
||||
class ParseException extends \Exception {}
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
/**
|
||||
* This is a library for parsing RIS files.
|
||||
*
|
||||
* LibRIS::RISReader() is the main parser.
|
||||
* LibRIS::RISWriter() can generate RIS data.
|
||||
* LibRIS::RISTags() contains useful RIS information.
|
||||
*
|
||||
* @see http://www.refman.com/support/risformat_intro.asp
|
||||
*/
|
||||
|
||||
namespace LibRIS;
|
||||
|
||||
/**
|
||||
* The main class for parsing RIS files.
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* <?php
|
||||
*
|
||||
* use \LibRIS\RISReader;
|
||||
*
|
||||
* $reader = new RISReader();
|
||||
*
|
||||
* // Parse a file of RIS data.
|
||||
* $reader->parseFile('path/to/file.ris');
|
||||
*
|
||||
* // Parse a string containing RIS data.
|
||||
* $reader->parseString($someRisString);
|
||||
*
|
||||
* // Parse an array of lines.
|
||||
* $reader->parseArray($arrayOfRISDirectives);
|
||||
*
|
||||
* // Get an associative array of records.
|
||||
* $array = $reader->getRecords();
|
||||
*
|
||||
* // Dump the records to STDOUT
|
||||
* $reader->printRecords();
|
||||
*
|
||||
* ?>
|
||||
* @endcode
|
||||
*
|
||||
* The data structure generated by this class is of the form
|
||||
* @code
|
||||
* <?php
|
||||
* array(
|
||||
* [0] => array(
|
||||
* 'T1' => array('title one', 'title 2'),
|
||||
* 'TY' => array('JOUR'),
|
||||
* // Other tags and their values.
|
||||
* ),
|
||||
* [1] => array(
|
||||
* 'T1' => array('another entry'),
|
||||
* 'TY' => array('JOUR'),
|
||||
* ),
|
||||
* );
|
||||
* ?>
|
||||
* @endcode
|
||||
*/
|
||||
class RISReader {
|
||||
|
||||
const RIS_EOL = "\r\n";
|
||||
const LINE_REGEX = '/^(([A-Z1-9]{2})\s+-(.*))|(.*)$/';
|
||||
|
||||
protected $data = NULL;
|
||||
|
||||
public function __construct($options = array()) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an RIS file.
|
||||
*
|
||||
* This will parse the file and return a data structure representing the
|
||||
* record.
|
||||
*
|
||||
* @param string $filename
|
||||
* The full path to the file to parse.
|
||||
* @param StreamContext $context
|
||||
* The stream context (in desired) for handling the file.
|
||||
* @retval array
|
||||
* An indexed array of individual sources, each of which is an
|
||||
* associative array of entry details. (See LibRIS)
|
||||
*/
|
||||
public function parseFile($filename, $context = NULL) {
|
||||
if (!is_file($filename)) {
|
||||
throw new ParseException(sprintf('File %s not found.', htmlentities($filename)));
|
||||
}
|
||||
$flags = FILE_SKIP_EMPTY_LINES | FILE_TEXT;
|
||||
$contents = file($filename, $flags, $context);
|
||||
|
||||
$this->parseArray($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string of RIS data.
|
||||
*
|
||||
* This will parse an RIS record into a representative data structure.
|
||||
*
|
||||
* @param string $string
|
||||
* RIS-formatted data in a string.
|
||||
* @param StreamContext $context
|
||||
* The stream context (in desired) for handling the file.
|
||||
* @retval array
|
||||
* An indexed array of individual sources, each of which is an
|
||||
* associative array of entry details. (See {@link LibRIS})
|
||||
*/
|
||||
public function parseString($string) {
|
||||
$contents = explode (RISReader::RIS_EOL, $string);
|
||||
$this->parseArray($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an array of lines and parse them into an RIS record.
|
||||
*/
|
||||
protected function parseArray($lines) {
|
||||
$recordset = array();
|
||||
|
||||
// Do any cleaning and normalizing.
|
||||
$this->cleanData($lines);
|
||||
|
||||
$record = array();
|
||||
$lastTag = NULL;
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
$matches = array();
|
||||
|
||||
preg_match(self::LINE_REGEX, $line, $matches);
|
||||
if (!empty($matches[3])) {
|
||||
$lastTag = $matches[2];
|
||||
$record[$matches[2]][] = trim($matches[3]);
|
||||
}
|
||||
// End record and prep a new one.
|
||||
elseif (!empty($matches[2]) && $matches[2] == 'ER') {
|
||||
$lastTag = NULL;
|
||||
$recordset[] = $record;
|
||||
$record = array();
|
||||
}
|
||||
elseif (!empty($matches[4])) {
|
||||
// Append to the last one.
|
||||
// We skip leading info (like BOMs).
|
||||
if (!empty($lastTag)) {
|
||||
$lastEntry = count($record[$lastTag]) - 1;
|
||||
// We trim because some encoders add tabs or multiple spaces.
|
||||
// Standard is silent on how this should be handled.
|
||||
$record[$lastTag][$lastEntry] .= ' ' . trim($matches[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($record)) $recordset[] = $record;
|
||||
|
||||
$this->data = $recordset;
|
||||
}
|
||||
|
||||
public function getRecords() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function printRecords() {
|
||||
$format = "%s:\n\t%s\n";
|
||||
foreach ($this->data as $record) {
|
||||
foreach ($record as $key => $values) {
|
||||
foreach ($values as $value) {
|
||||
printf($format, RISTags::describeTag($key), $value);
|
||||
}
|
||||
}
|
||||
|
||||
print PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the data before processing.
|
||||
*
|
||||
* @param array $lines
|
||||
* Indexed array of lines of data.
|
||||
*/
|
||||
protected function cleanData(&$lines) {
|
||||
|
||||
if (empty($lines)) return;
|
||||
|
||||
// Currently, we only need to strip a BOM if it exists.
|
||||
// Thanks to Derik Badman (http://madinkbeard.com/) for finding the
|
||||
// bug and suggesting this fix:
|
||||
// http://blog.philipp-michels.de/?p=32
|
||||
$first = $lines[0];
|
||||
if (substr($first, 0, 3) == pack('CCC', 0xef, 0xbb, 0xbf)) {
|
||||
$lines[0] = substr($first, 3);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
namespace LibRIS;
|
||||
|
||||
class RISTags {
|
||||
|
||||
public static function getTags() {
|
||||
return array_keys(self::$tagMap);
|
||||
}
|
||||
|
||||
public static function getTypes() {
|
||||
return array_keys(self::$typeMap);
|
||||
}
|
||||
|
||||
public static function describeTag($tag) {
|
||||
return self::$tagMap[$tag];
|
||||
}
|
||||
|
||||
public static function describeType($type) {
|
||||
return self::$typeMap[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* The definitive list of all fields.
|
||||
* @var array
|
||||
* @see http://en.wikipedia.org/wiki/RIS_%28file_format%29
|
||||
* @see http://www.refman.com/support/risformat_intro.asp
|
||||
*/
|
||||
public static $tagMap = array(
|
||||
'TY' => 'Type',
|
||||
'ID' => 'Reference ID',
|
||||
'T1' => 'Title',
|
||||
'TI' => 'Book title',
|
||||
'CT' => 'Title of unpublished reference',
|
||||
'A1' => 'Primary author',
|
||||
'A2' => 'Secondary author',
|
||||
'AU' => 'Author',
|
||||
'Y1' => 'Primary date',
|
||||
'PY' => 'Publication year',
|
||||
'N1' => 'Notes',
|
||||
'KW' => 'Keywords',
|
||||
'RP' => 'Reprint status',
|
||||
'SP' => 'Start page',
|
||||
'EP' => 'Ending page',
|
||||
'JF' => 'Periodical full name',
|
||||
'JO' => 'Periodical standard abbreviation',
|
||||
'JA' => 'Periodical in which article was published',
|
||||
'J1' => 'Periodical name - User abbreviation 1',
|
||||
'J2' => 'Periodical name - User abbreviation 2',
|
||||
'VL' => 'Volume',
|
||||
'IS' => 'Issue',
|
||||
'T2' => 'Title secondary',
|
||||
'CY' => 'City of Publication',
|
||||
'PB' => 'Publisher',
|
||||
'U1' => 'User 1',
|
||||
'U2' => 'User 2',
|
||||
'U3' => 'User 3',
|
||||
'U4' => 'User 4',
|
||||
'U5' => 'User 5',
|
||||
'T3' => 'Title series',
|
||||
'N2' => 'Abstract',
|
||||
'SN' => 'ISSN/ISBN/ASIN',
|
||||
'AV' => 'Availability',
|
||||
'M1' => 'Misc. 1',
|
||||
'M2' => 'Misc. 2',
|
||||
'M3' => 'Misc. 3',
|
||||
'AD' => 'Address',
|
||||
'UR' => 'URL',
|
||||
'L1' => 'Link to PDF',
|
||||
'L2' => 'Link to Full-text',
|
||||
'L3' => 'Related records',
|
||||
'L4' => 'Images',
|
||||
'ER' => 'End of Reference',
|
||||
|
||||
// Unsure about the origin of these
|
||||
'Y2' => 'Primary date 2',
|
||||
'BT' => 'Institution [?]',
|
||||
);
|
||||
|
||||
public static $tagDescriptions = array(
|
||||
'TY' => 'Type of reference (must be the first tag)',
|
||||
'ID' => 'Reference ID (not imported to reference software)',
|
||||
'T1' => 'Primary title',
|
||||
'TI' => 'Book title',
|
||||
'CT' => 'Title of unpublished reference',
|
||||
'A1' => 'Primary author',
|
||||
'A2' => 'Secondary author (each name on separate line)',
|
||||
'AU' => 'Author (syntax. Last name, First name, Suffix)',
|
||||
'Y1' => 'Primary date',
|
||||
'PY' => 'Publication year (YYYY/MM/DD)',
|
||||
'N1' => 'Notes ',
|
||||
'KW' => 'Keywords (each keyword must be on separate line preceded KW -)',
|
||||
'RP' => 'Reprint status (IN FILE, NOT IN FILE, ON REQUEST (MM/DD/YY))',
|
||||
'SP' => 'Start page number',
|
||||
'EP' => 'Ending page number',
|
||||
'JF' => 'Periodical full name',
|
||||
'JO' => 'Periodical standard abbreviation',
|
||||
'JA' => 'Periodical in which article was published',
|
||||
'J1' => 'Periodical name - User abbreviation 1',
|
||||
'J2' => 'Periodical name - User abbreviation 2',
|
||||
'VL' => 'Volume number',
|
||||
'IS' => 'Issue number',
|
||||
'T2' => 'Title secondary',
|
||||
'CY' => 'City of Publication',
|
||||
'PB' => 'Publisher',
|
||||
'U1' => 'User definable 1',
|
||||
'U2' => 'User definable 2',
|
||||
'U3' => 'User definable 3',
|
||||
'U4' => 'User definable 4',
|
||||
'U5' => 'User definable 5',
|
||||
'T3' => 'Title series',
|
||||
'N2' => 'Abstract',
|
||||
'SN' => 'ISSN/ISBN (e.g. ISSN XXXX-XXXX)',
|
||||
'AV' => 'Availability',
|
||||
'M1' => 'Misc. 1',
|
||||
'M2' => 'Misc. 2',
|
||||
'M3' => 'Misc. 3',
|
||||
'AD' => 'Address',
|
||||
'UR' => 'Web/URL',
|
||||
'L1' => 'Link to PDF',
|
||||
'L2' => 'Link to Full-text',
|
||||
'L3' => 'Related records',
|
||||
'L4' => 'Images',
|
||||
'ER' => 'End of Reference (must be the last tag)',
|
||||
);
|
||||
|
||||
/**
|
||||
* Map of all types (tag TY) defined for RIS.
|
||||
* @var array
|
||||
* @see http://en.wikipedia.org/wiki/RIS_%28file_format%29
|
||||
* @see http://www.refman.com/support/risformat_intro.asp
|
||||
*/
|
||||
public static $typeMap = array(
|
||||
'ABST' => 'Abstract',
|
||||
'ADVS' => 'Audiovisual material',
|
||||
'ART' => 'Art Work',
|
||||
'BOOK' => 'Whole book',
|
||||
'CASE' => 'Case',
|
||||
'CHAP' => 'Book chapter',
|
||||
'COMP' => 'Computer program',
|
||||
'CONF' => 'Conference proceeding',
|
||||
'CTLG' => 'Catalog',
|
||||
'DATA' => 'Data file',
|
||||
'ELEC' => 'Electronic Citation',
|
||||
'GEN' => 'Generic',
|
||||
'HEAR' => 'Hearing',
|
||||
'ICOMM' => 'Internet Communication',
|
||||
'INPR' => 'In Press',
|
||||
'JFULL' => 'Journal (full)',
|
||||
'JOUR' => 'Journal',
|
||||
'MAP' => 'Map',
|
||||
'MGZN' => 'Magazine article',
|
||||
'MPCT' => 'Motion picture',
|
||||
'MUSIC' => 'Music score',
|
||||
'NEWS' => 'Newspaper',
|
||||
'PAMP' => 'Pamphlet',
|
||||
'PAT' => 'Patent',
|
||||
'PCOMM' => 'Personal communication',
|
||||
'RPRT' => 'Report',
|
||||
'SER' => 'Serial publication',
|
||||
'SLIDE' => 'Slide',
|
||||
'SOUND' => 'Sound recording',
|
||||
'STAT' => 'Statute',
|
||||
'THES' => 'Thesis/Dissertation',
|
||||
'UNPB' => 'Unpublished work',
|
||||
'VIDEO' => 'Video recording',
|
||||
);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
namespace LibRIS;
|
||||
|
||||
/**
|
||||
* Class for writing RIS data.
|
||||
*
|
||||
* General usage:
|
||||
* @code
|
||||
* <?php
|
||||
* use \LibRIS\RISWriter;
|
||||
*
|
||||
* $writer = new RISWriter();
|
||||
*
|
||||
* // Write an associative array of records to a string.
|
||||
* $str = $writer->writeRecords($records);
|
||||
* ?>
|
||||
* @endcode
|
||||
*/
|
||||
class RISWriter {
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* Write a series of records to a single RIS string.
|
||||
*
|
||||
* @param array $records
|
||||
* An array in the format generated by RISReader::parseFile()
|
||||
* @retval string
|
||||
* The record as a string.
|
||||
*/
|
||||
public function writeRecords($records) {
|
||||
$buffer = array();
|
||||
foreach ($records as $record) {
|
||||
$buffer[] = $this->writeRecord($record);
|
||||
}
|
||||
return implode(RISReader::RIS_EOL, $buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a single record as an RIS string.
|
||||
*
|
||||
* The record should be an associative array of tags to values.
|
||||
*
|
||||
* @param array $tags
|
||||
* An associative array of key => array(value1, value2,...).
|
||||
* @retval string
|
||||
* The record as a string.
|
||||
*/
|
||||
public function writeRecord($tags) {
|
||||
$buffer = array();
|
||||
$fmt = '%s - %s';
|
||||
|
||||
$buffer[] = sprintf($fmt, 'TY', $tags['TY'][0]);
|
||||
unset($tags['TY']);
|
||||
|
||||
foreach ($tags as $tag => $values) {
|
||||
foreach ($values as $value) {
|
||||
$buffer[] = sprintf($fmt, $tag, $value);
|
||||
}
|
||||
}
|
||||
$buffer[] = 'ER - ';
|
||||
|
||||
return implode(RISReader::RIS_EOL, $buffer);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue