Index: TemplateAdventures.i18n.magic.php =================================================================== --- TemplateAdventures.i18n.magic.php (revision 87250) +++ TemplateAdventures.i18n.magic.php (working copy) @@ -5,6 +5,14 @@ $magicWords['en'] = array( # citation magic words 'ta_citation' => array( 1, 'citation' ), + 'ta_s_default' => array( 0, 'default' ), + 'ta_s_Chicago' => array( 0, 'Chicago', 'chicago' ), + 'ta_s_APA' => array( 0, 'APA', 'apa' ), + 'ta_s_Bluebook' => array( 0, 'Bluebook', 'bluebook' ), + 'ta_t_book' => array( 0, 'book' ), + 'ta_t_journal' => array( 0, 'journal' ), + 'ta_t_news' => array( 0, 'news' ), + 'ta_t_web' => array( 0, 'web' ), 'ta_cc_author' => array( 0, 'author' ), 'ta_cc_authorsurname' => array( 0, 'surname', 'last' ), 'ta_cc_authorgiven' => array( 0, 'given', 'first' ), Index: TemplateAdventures.php =================================================================== --- TemplateAdventures.php (revision 87250) +++ TemplateAdventures.php (working copy) @@ -5,7 +5,7 @@ * suffers greatly from this. * * - * Copyright (C) 2010 'Svip', 'MZMcBride' and others. + * Copyright (c) 2010-11 'Svip', 'MZMcBride', 'COGDEN', and others. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,8 +50,11 @@ $wgExtensionMessagesFiles['TemplateAdventures'] = "$dir/TemplateAdventures.i18n.php"; $wgExtensionMessagesFiles['TemplateAdventuresMagic'] = "$dir/TemplateAdventures.i18n.magic.php"; +# Map class name to filename, for autoloading $wgAutoloadClasses['Citation'] = $dir . '/Templates/Citation.php'; +$wgAutoloadClasses['CitationChicago'] = $dir . '/Templates/CitationChicago.php'; +# Define a setup function $wgHooks['ParserFirstCallInit'][] = 'TemplateAdventures::onParserFirstCallInit'; $wgParserTestFiles[] = dirname( __FILE__ ) . "/taParserTests.txt"; @@ -59,16 +62,20 @@ class TemplateAdventures { public static function onParserFirstCallInit( $parser ) { + # Register hooks linking i18n parser function names to appropriate methods $parser->setFunctionHook( 'ta_citation', array( __CLASS__, 'citation' ), SFH_OBJECT_ARGS ); + # Hooks to future parser functions other than "citation" will go here. return true; } /** - * Render {{#citation:}} + * Render {{#citation:}}. + * Unnamed parameter is extracted to determine which is the appropriate + * child class of itation renderer classes. * * @param $parser Parser * @param $frame PPFrame_DOM @@ -78,20 +85,56 @@ public static function citation( $parser, $frame, $args ) { if ( count( $args ) == 0 ) return ''; - $obj = new Citation( $parser, $frame, $args ); + # Unnamed parameter (after the colon) has special significance. + # Separate out unnamed parameter + $primary = trim( $frame->expand( array_shift ( $args) ) ); + $styleMagicWords = new MagicWordArray ( array ( + 'ta_s_default', + 'ta_s_Chicago', + 'ta_s_APA', + 'ta_s_Bluebook' + )); + $primary = $styleMagicWords->matchStartToEnd( $primary ); + switch ($primary) { + # Standard Chicago Manual of Style + case 'ta_s_Chicago': + $obj = new CitationChicago( $parser, $frame, $args ); + break; +/* # Standard APA Style + case 'ta_s_APA': + $obj = new CitationAPA( $parser, $frame, $args ); + break;*/ + # Standard Bluebook style + case 'ta_s_Bluebook': + $obj = new CitationBluebook( $parser, $frame, $args ); + break; + # Default style, which is non-standard APA + case 'ta_s_default': + default: + $obj = new Citation( $parser, $frame, $args ); + } + + #render the output, and return $obj->render(); - return $obj->output(); } } -class TemplateAdventureBasic { +abstract class TemplateAdventureBasic { - protected $mParser; - protected $mFrame; - public $mArgs; + # output of the parser protected $mOutput; + # array of (i18n option) keyed to (value) + private $options = array (); + + # array of unnamed i18n parameters + private $unnamed = array (); + + private $mParser; + private $mFrame; + private $mArgs; + /** * Constructor * @param $parser Parser @@ -102,40 +145,91 @@ $this->mParser = $parser; $this->mFrame = $frame; $this->mArgs = $args; + $this->readOptions(); } /** * Outputter */ - public function output() { + final public function output() { + /* # De-comment for testing of parsing. + foreach ( $this->options as $option ) { + $this->mOutput .= "("; + $this->mOutput .= implode ( ": ", $option ); + $this->mOutput .= ") "; + }; + $this->mOutput .= "keys: "; + $this->mOutput .= implode ( ', ', array_keys($this->unnamed)); + $this->mOutput .= "values: "; + $this->mOutput .= implode ( ', ', array_values($this->unnamed)); + $this->mOutput .= "unnamed parameters (if any): "; + $this->mOutput .= implode ( ', ', $this->unnamed); + # End test */ return $this->mOutput; } /** - * Do stuff. + * Lists all recognized and applied options. + * @return array of (option, value) tuples of strings */ - public function render() { - return; - } + final public function getOptions() { + return $this->options; + } /** + * Lists all recognized and applied unnamed parameters. + * @return array of unnamed parameters + */ + final public function getUnamedParameters() { + return $this->unnamed; + } + + /** + * Sets private data based on the value of each variable, + * Called by constructor sequentially for each argument having the form of A = B + * + * @param $var = the variable + * @param $value = the value + * @return i18n magic word if a known option, Null if not + */ + abstract protected function optionParse( $var, $value ); + + /** + * Sets private data based on the value of each unnamed parameter, + * Called by constructor sequentially for each unnamed parameter. + * + * @param $value = the value + * @return i18n parameter if a known parameter, Null if not + */ + protected function unnamedParameterParse( $value ) { + return Null; + } + + /** + * Does the work of rendering the output based on private parsed data. + */ + abstract public function render(); + + /** * Read options from $this->mArgs. Let the children handle the options. */ - protected function readOptions ( ) { + private function readOptions () { $args = $this->mArgs; - # an array of items not options - $this->mReaditems = array(); - - # first input is a bit different than the rest, - # so we'll treat that differently - $primary = trim( $this->mFrame->expand( array_shift( $args ) ) ); - $primary = $this->handlePrimaryItem( $primary ); - - # check the rest for options + # check args for options foreach( $args as $arg ) { $item = $this->handleInputItem( $arg ); + $var = $item[0]; + $value = $item[1]; + # if an unnamed parameter or valid option, set private data + if ( $var ) { + if ( $varI18n = $this->optionParse( $var, $value ) ) { + $this->options[$varI18n] = $value; + } # else continue; + } elseif ( $valueI18n = $this->unnamedParameterParse( $value ) ) { + $this->unnamed[] = $valueI18n; + } } } @@ -148,7 +242,7 @@ * @param $arg String Argument * @return String if element, else return false */ - protected function handleInputItem( $arg ) { + private function handleInputItem( $arg ) { if ( $arg instanceof PPNode_DOM ) { $bits = $arg->splitArg(); $index = $bits['index']; @@ -156,7 +250,8 @@ $var = trim( $this->mFrame->expand( $bits['name'] ) ); $value = trim( $this->mFrame->expand( $bits['value'] ) ); } else { # Not found - return trim( $this->mFrame->expand( $arg ) ); + $var = NULL; + $value = trim( $this->mFrame->expand( $arg ) ); } } else { $parts = array_map( 'trim', explode( '=', $arg, 2 ) ); @@ -164,45 +259,10 @@ $var = $parts[0]; $value = $parts[1]; } else { # Not found - return $arg; + $var = NULL; + $value = $arg; } } - # Still here? Then it must be an option - return $this->optionParse( $var, $value ); + return array ( $var, $value ); } - - /** - * This functions handles the primary item. It is supposed to be - * overwriteable - * - * @param $arg String Argument - * @return String if understood, else return false - */ - protected function handlePrimaryItem( $arg ) { - return false; - } - - /** - * Parse the option. - * This should be rewritten in classes inheriting this class. - * - * @param $var - * @param $value - * @return False if option else element - */ - protected function optionParse( $var, $value ) { - return $arg instanceof PPNode_DOM - ? trim( $this->mFrame->expand( $arg ) ) - : $arg; - } - - /** - * Using magic to store all known names for each option - * - * @param $input String - * @return The option found; otherwise false - */ - protected function parseOptionName( $value ) { - return false; - } } Property changes on: Templates ___________________________________________________________________ Added: svn:ignore + CitationBluebook.php CitationDefault.php Index: Templates/Citation.php =================================================================== --- Templates/Citation.php (revision 87250) +++ Templates/Citation.php (working copy) @@ -2,8 +2,10 @@ class Citation extends TemplateAdventureBasic { - private $citeType = null; # type of citation, e.g. 'news' - # currently only 'news' supported. + private $citeType = Null; + + protected $typeMagicWords; + private $dSeparators = array( # separators between names, items, etc. 'section' => ',', 'end' => '.', @@ -31,7 +33,7 @@ 'title' => null, 'transtitle' => null, # translated title (if original title is # in a foreign language). - 'transitalic' => null, # translated title in italic + 'transitalic' => null, # translated title in italic 'includedwork' => null, 'type' => null, # the title type 'note' => null, @@ -95,7 +97,6 @@ * Our construct function. */ public function __construct( $parser, $frame, $args ) { - parent::__construct($parser, $frame, $args); # init data $this->dSeparators['section'] = wfMsg ( 'ta-citesep-section' ); $this->dSeparators['author'] = wfMsg ( 'ta-citesep-author' ); @@ -103,7 +104,18 @@ $this->dSeparators['authorlast'] = wfMsg ( 'ta-citesep-authorlast' ); $this->dSeparators['beforepublication'] = wfMsg ( 'ta-citesep-beforepublication' ); $this->dSeparators['end'] = wfMsg ( 'ta-citesep-end' ); - $this->readOptions( ); + $this->typeMagicWords = new MagicWordArray ( array + ('ta_t_book', + 'ta_t_journal', + 'ta_t_news', + 'ta_t_web' )); + + # call parent constructor + parent::__construct($parser, $frame, $args); + + if ( $this->citeType == Null ) $this->citeType = $this->inferCiteType(); + + # further parsing $this->parseData(); } @@ -538,7 +550,7 @@ # |{{{ID}}} # }} # isbn - if ( $this->citeType == 'book' + if ( ($this->citeType == 'book' ) && $this->notNull ( $this->dBook['isbn'] ) ) { $this->addSection ( wfMsg ( 'ta-citebookisbn', $this->dBook['isbn'], @@ -621,9 +633,9 @@ # TODO $this->mOutput = ''; + + $len = count ( $this->mSections ); - $len = count ( $this->mSections ); - foreach ( $this->mSections as $i => $section ) { $this->mOutput .= $section[0]; if ( ( $i + 1 < $len ) @@ -768,8 +780,7 @@ } /** - * This function parses the data the given to it during the readOptions() - * run. Basically to disregard data and such that has been found to be + * Disregards data and such that has been found to be * outside the allowed logic of this 'template'. */ private function parseData() { @@ -989,15 +1000,16 @@ } /** - * Checks whether the data provided is a known option. + * Sets private data based on the value of each variable, + * Called by constructor sequentially for each argument having the form of A = B * - * @param $var The variable - * @param $value The value - * @return True if option, false if not. - */ + * @param $var = the variable + * @param $value = the value + * @return i18n magic word if a known option, Null if not + */ protected function optionParse( $var, $value ) { if ( !$this->notNull ( $value ) ) - return; + return Null; $name = self::parseOptionName( $var ); switch ( $name[0] ) { case 'author': @@ -1045,26 +1057,12 @@ break; default: # Wasn't an option after all - return false; + return Null; } - return true; + return 'ta_cc_' . $name[0]; } /** - * This function handles the first item of the variable. For {{#citation:}} - * the first item defines the type of the citation; which is important the - * rendering of the function. - * - * Right now only 'news' is an acceptable citation type. - * - * @param $item The raw item. - */ - protected function handlePrimaryItem( $item ) { - if ( in_array ( $item, array ( 'web', 'news', 'journal', 'book' ) ) ) - $this->citeType = $item; - } - - /** * This one parses the variable name given to optionParse to figure out * whether this is a known parameter to this template. * @@ -1114,4 +1112,37 @@ # blimey, so not an option!? return array( false, null ); } + + /** + * Sets $citeType based on the first valid unnamed parameter, + * Called by constructor sequentially for each unnamed parameter. + * + * @param $value = the value + * @return i18n parameter if a known parameter, Null if not + */ + protected function unnamedParameterParse( $value ) { + # If $citeType already set, then don't set it again. + if ( $this->citeType ) return Null; + + if ( $citeTypeLong = $this->typeMagicWords->matchStartToEnd( $value ) ) { + $this->citeType = str_replace( 'ta_t_', '', $citeTypeLong ); + return $citeTypeLong; + } else return Null; + } + + /** + * Infer $citeType based on the first valid unnamed parameter, + * Called by constructor sequentially for each unnamed parameter. + * + * @param $value = the value + * @return i18n parameter if $citeType inferred, Null if not + */ + protected function inferCiteType() { + $options = $this->getOptions(); + + if ( array_key_exists( 'ta_cc_journal', $options ) ) { + return 'journal'; + } + } + } Index: Templates/CitationChicago.php =================================================================== --- Templates/CitationChicago.php (revision 87250) +++ Templates/CitationChicago.php (working copy) @@ -1,9 +1,9 @@ ',', 'end' => '.', @@ -31,7 +31,7 @@ 'title' => null, 'transtitle' => null, # translated title (if original title is # in a foreign language). - 'transitalic' => null, # translated title in italic + 'transitalic' => null, # translated title in italic 'includedwork' => null, 'type' => null, # the title type 'note' => null, @@ -95,7 +95,6 @@ * Our construct function. */ public function __construct( $parser, $frame, $args ) { - parent::__construct($parser, $frame, $args); # init data $this->dSeparators['section'] = wfMsg ( 'ta-citesep-section' ); $this->dSeparators['author'] = wfMsg ( 'ta-citesep-author' ); @@ -103,7 +102,9 @@ $this->dSeparators['authorlast'] = wfMsg ( 'ta-citesep-authorlast' ); $this->dSeparators['beforepublication'] = wfMsg ( 'ta-citesep-beforepublication' ); $this->dSeparators['end'] = wfMsg ( 'ta-citesep-end' ); - $this->readOptions( ); + # call parent constructor + parent::__construct($parser, $frame, $args); + # further parsing $this->parseData(); } @@ -538,7 +539,7 @@ # |{{{ID}}} # }} # isbn - if ( $this->citeType == 'book' + if ( ($this->citeType == 'book' ) && $this->notNull ( $this->dBook['isbn'] ) ) { $this->addSection ( wfMsg ( 'ta-citebookisbn', $this->dBook['isbn'], @@ -623,7 +624,7 @@ $this->mOutput = ''; $len = count ( $this->mSections ); - + foreach ( $this->mSections as $i => $section ) { $this->mOutput .= $section[0]; if ( ( $i + 1 < $len ) @@ -989,15 +990,16 @@ } /** - * Checks whether the data provided is a known option. + * Sets private data based on the value of each variable, + * Called by constructor sequentially for each argument having the form of A = B * - * @param $var The variable - * @param $value The value - * @return True if option, false if not. - */ + * @param $var = the variable + * @param $value = the value + * @return i18n magic word if a known option, Null if not + */ protected function optionParse( $var, $value ) { if ( !$this->notNull ( $value ) ) - return; + return Null; $name = self::parseOptionName( $var ); switch ( $name[0] ) { case 'author': @@ -1045,26 +1047,12 @@ break; default: # Wasn't an option after all - return false; + return Null; } - return true; + return 'ta_cc_' . $name[0]; } /** - * This function handles the first item of the variable. For {{#citation:}} - * the first item defines the type of the citation; which is important the - * rendering of the function. - * - * Right now only 'news' is an acceptable citation type. - * - * @param $item The raw item. - */ - protected function handlePrimaryItem( $item ) { - if ( in_array ( $item, array ( 'web', 'news', 'journal', 'book' ) ) ) - $this->citeType = $item; - } - - /** * This one parses the variable name given to optionParse to figure out * whether this is a known parameter to this template. * @@ -1114,4 +1102,28 @@ # blimey, so not an option!? return array( false, null ); } + + /** + * Sets $citeType based on the first valid unnamed parameter, + * Called by constructor sequentially for each unnamed parameter. + * + * @param $value = the value + * @return i18n parameter if a known parameter, Null if not + */ + protected function unnamedParameterParse( $value ) { + # If $citeType already set, then don't set it again. + if ( $this->citeType ) return Null; + + $typeMagicWords = new MagicWordArray ( array + ('ta_t_book', + 'ta_t_journal', + 'ta_t_news', + 'ta_t_web' ) + ); + if ( $citeTypeLong = $typeMagicWords->matchStartToEnd( $value ) ) { + $this->citeType = str_replace( 'ta_t_', '', $citeTypeLong ); + return $citeTypeLong; + } else return Null; + } + }