TYPO3 API  SVNRelease
DocbookGenerator.php
Go to the documentation of this file.
00001 <?php
00002 
00003 /*                                                                        *
00004  * This script belongs to the FLOW3 package "Fluid".                      *
00005  *                                                                        *
00006  * It is free software; you can redistribute it and/or modify it under    *
00007  * the terms of the GNU Lesser General Public License as published by the *
00008  * Free Software Foundation, either version 3 of the License, or (at your *
00009  * option) any later version.                                             *
00010  *                                                                        *
00011  * This script is distributed in the hope that it will be useful, but     *
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
00013  * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
00014  * General Public License for more details.                               *
00015  *                                                                        *
00016  * You should have received a copy of the GNU Lesser General Public       *
00017  * License along with the script.                                         *
00018  * If not, see http://www.gnu.org/licenses/lgpl.html                      *
00019  *                                                                        *
00020  * The TYPO3 project - inspiring people to share!                         *
00021  *                                                                        */
00022 
00023 /**
00024  * XML Schema (XSD) Generator. Will generate an XML schema which can be used for autocompletion
00025  * in schema-aware editors like Eclipse XML editor.
00026  *
00027  * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
00028  */
00029 class Tx_Fluid_Service_DocbookGenerator extends Tx_Fluid_Service_AbstractGenerator {
00030 
00031     /**
00032      * Generate the XML Schema definition for a given namespace.
00033      *
00034      * @param string $namespace Namespace identifier to generate the XSD for, without leading Backslash.
00035      * @return string XML Schema definition
00036      * @author Sebastian Kurfürst <sebastian@typo3.org>
00037      */
00038     public function generateDocbook($namespace) {
00039         if (substr($namespace, -1) !== Tx_Fluid_Fluid::NAMESPACE_SEPARATOR) {
00040             $namespace .= Tx_Fluid_Fluid::NAMESPACE_SEPARATOR;
00041         }
00042 
00043         $classNames = $this->getClassNamesInNamespace($namespace);
00044 
00045         $xmlRootNode = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>
00046 <section version="5.0" xmlns="http://docbook.org/ns/docbook"
00047          xml:id="fluid.usermanual.standardviewhelpers"
00048          xmlns:xl="http://www.w3.org/1999/xlink"
00049          xmlns:xi="http://www.w3.org/2001/XInclude"
00050          xmlns:xhtml="http://www.w3.org/1999/xhtml"
00051          xmlns:svg="http://www.w3.org/2000/svg"
00052          xmlns:ns="http://docbook.org/ns/docbook"
00053          xmlns:mathml="http://www.w3.org/1998/Math/MathML">
00054     <title>Standard View Helper Library</title>
00055 
00056     <para>Should be autogenerated from the tags.</para>
00057 </section>');
00058 
00059         foreach ($classNames as $className) {
00060             $this->generateXmlForClassName($className, $namespace, $xmlRootNode);
00061         }
00062 
00063         return $xmlRootNode->asXML();
00064     }
00065 
00066     /**
00067      * Generate the XML Schema for a given class name.
00068      *
00069      * @param string $className Class name to generate the schema for.
00070      * @param string $namespace Namespace prefix. Used to split off the first parts of the class name.
00071      * @param SimpleXMLElement $xmlRootNode XML root node where the xsd:element is appended.
00072      * @return void
00073      * @author Sebastian Kurfürst <sebastian@typo3.org>
00074      */
00075     protected function generateXmlForClassName($className, $namespace, SimpleXMLElement $xmlRootNode) {
00076         $reflectionClass = new Tx_Extbase_Reflection_ClassReflection($className);
00077         if (!$reflectionClass->isSubclassOf($this->abstractViewHelperReflectionClass)) {
00078             return;
00079         }
00080 
00081         $tagName = $this->getTagNameForClass($className, $namespace);
00082 
00083         $docbookSection = $xmlRootNode->addChild('section');
00084 
00085         $docbookSection->addChild('title', $tagName);
00086         $this->docCommentParser->parseDocComment($reflectionClass->getDocComment());
00087         $this->addDocumentation($this->docCommentParser->getDescription(), $docbookSection);
00088 
00089         $argumentsSection = $docbookSection->addChild('section');
00090         $argumentsSection->addChild('title', 'Arguments');
00091         $this->addArguments($className, $argumentsSection);
00092 
00093         return $docbookSection;
00094     }
00095 
00096     /**
00097      * Add attribute descriptions to a given tag.
00098      * Initializes the view helper and its arguments, and then reads out the list of arguments.
00099      *
00100      * @param string $className Class name where to add the attribute descriptions
00101      * @param SimpleXMLElement $docbookSection DocBook section to add the attributes to.
00102      * @return void
00103      * @author Sebastian Kurfürst <sebastian@typo3.org>
00104      */
00105     protected function addArguments($className, SimpleXMLElement $docbookSection) {
00106         $viewHelper = $this->instanciateViewHelper($className);
00107         $argumentDefinitions = $viewHelper->prepareArguments();
00108 
00109         if (count($argumentDefinitions) === 0) {
00110             $docbookSection->addChild('para', 'No arguments defined.');
00111             return;
00112         }
00113         $argumentsTable = $docbookSection->addChild('table');
00114         $argumentsTable->addChild('title', 'Arguments');
00115         $tgroup = $argumentsTable->addChild('tgroup');
00116         $tgroup['cols'] = 4;
00117         $this->addArgumentTableRow($tgroup->addChild('thead'), 'Name', 'Type', 'Required', 'Description', 'Default');
00118 
00119         $tbody = $tgroup->addChild('tbody');
00120 
00121         foreach ($argumentDefinitions as $argumentDefinition) {
00122             $this->addArgumentTableRow($tbody, $argumentDefinition->getName(), $argumentDefinition->getType(), ($argumentDefinition->isRequired()?'yes':'no'), $argumentDefinition->getDescription(), $argumentDefinition->getDefaultValue());
00123         }
00124     }
00125 
00126     /**
00127      * Instantiate a view helper.
00128      *
00129      * @param string $className
00130      * @return object
00131      */
00132     protected function instanciateViewHelper($className) {
00133         return $this->objectManager->get($className);
00134     }
00135 
00136     /**
00137      * @param SimpleXMLElement $parent
00138      * @param string $name
00139      * @param string $type
00140      * @param boolean $required
00141      * @param string $description
00142      * @param string $default
00143      * @return void
00144      */
00145     private function addArgumentTableRow(SimpleXMLElement $parent, $name, $type, $required, $description, $default) {
00146         $row = $parent->addChild('row');
00147 
00148         $row->addChild('entry', $name);
00149         $row->addChild('entry', $type);
00150         $row->addChild('entry', $required);
00151         $row->addChild('entry', $description);
00152         $row->addChild('entry', (string)$default);
00153     }
00154 
00155     /**
00156      * Add documentation XSD to a given XML node
00157      *
00158      * As Eclipse renders newlines only on new <xsd:documentation> tags, we wrap every line in a new
00159      * <xsd:documentation> tag.
00160      * Furthermore, eclipse strips out tags - the only way to prevent this is to have every line wrapped in a
00161      * CDATA block AND to replace the < and > with their XML entities. (This is IMHO not XML conformant).
00162      *
00163      * @param string $documentation Documentation string to add.
00164      * @param SimpleXMLElement $docbookSection Node to add the documentation to
00165      * @return void
00166      * @author Sebastian Kurfürst <sebastian@typo3.org>
00167      */
00168     protected function addDocumentation($documentation, SimpleXMLElement $docbookSection) {
00169         $splitRegex = '/^\s*(=[^=]+=)$/m';
00170         $regex = '/^\s*(=([^=]+)=)$/m';
00171 
00172         $matches = preg_split($splitRegex, $documentation, -1,  PREG_SPLIT_NO_EMPTY  |  PREG_SPLIT_DELIM_CAPTURE );
00173 
00174         $currentSection = $docbookSection;
00175         foreach ($matches as $singleMatch) {
00176             if (preg_match($regex, $singleMatch, $tmp)) {
00177                 $currentSection = $docbookSection->addChild('section');
00178                 $currentSection->addChild('title', trim($tmp[2]));
00179             } else {
00180                 $this->addText(trim($singleMatch), $currentSection);
00181             }
00182         }
00183     }
00184 
00185     /**
00186      * @param string $text
00187      * @param SimpleXMLElement $parentElement
00188      */
00189     protected function addText($text, SimpleXMLElement $parentElement) {
00190         $splitRegex = '/
00191         (<code(?:.*?)>
00192             (?:.*?)
00193         <\/code>)/xs';
00194 
00195         $regex = '/
00196         <code(.*?)>
00197             (.*?)
00198         <\/code>/xs';
00199         $matches = preg_split($splitRegex, $text, -1,  PREG_SPLIT_NO_EMPTY  |  PREG_SPLIT_DELIM_CAPTURE );
00200         foreach ($matches as $singleMatch) {
00201 
00202             if (preg_match($regex, $singleMatch, $tmp)) {
00203                 preg_match('/title="([^"]+)"/', $tmp[1], $titleMatch);
00204 
00205                 $example = $parentElement->addChild('example');
00206                 if (count($titleMatch)) {
00207                     $example->addChild('title', trim($titleMatch[1]));
00208                 } else {
00209                     $example->addChild('title', 'Example');
00210                 }
00211                 $this->addChildWithCData($example, 'programlisting', trim($tmp[2]));
00212             } else {
00213                 $textParts = explode("\n", $singleMatch);
00214                 foreach ($textParts as $text) {
00215                     if (trim($text) === '') continue;
00216                     $this->addChildWithCData($parentElement, 'para', trim($text));
00217                 }
00218             }
00219         }
00220     }
00221 }
00222 ?>