cfa3ee26d288ca210e34d7200fbf5733ca23a610
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / Service / DocbookGenerator.php
1 <?php
2 namespace TYPO3\CMS\Fluid\Service;
3
4 /* *
5 * This script is backported from the TYPO3 Flow package "TYPO3.Fluid". *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License, either version 3 *
9 * of the License, or (at your option) any later version. *
10 * *
11 * The TYPO3 project - inspiring people to share! *
12 * */
13
14 /**
15 * XML Schema (XSD) Generator. Will generate an XML schema which can be used for autocompletion
16 * in schema-aware editors like Eclipse XML editor.
17 */
18 class DocbookGenerator extends \TYPO3\CMS\Fluid\Service\AbstractGenerator {
19
20 /**
21 * Generate the XML Schema definition for a given namespace.
22 *
23 * @param string $namespace Namespace identifier to generate the XSD for, without leading Backslash.
24 * @return string XML Schema definition
25 */
26 public function generateDocbook($namespace) {
27 if (substr($namespace, -1) !== \TYPO3\CMS\Fluid\Fluid::NAMESPACE_SEPARATOR) {
28 $namespace .= \TYPO3\CMS\Fluid\Fluid::NAMESPACE_SEPARATOR;
29 }
30 $classNames = $this->getClassNamesInNamespace($namespace);
31 $xmlRootNode = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>
32 <section version="5.0" xmlns="http://docbook.org/ns/docbook"
33 xml:id="fluid.usermanual.standardviewhelpers"
34 xmlns:xl="http://www.w3.org/1999/xlink"
35 xmlns:xi="http://www.w3.org/2001/XInclude"
36 xmlns:xhtml="http://www.w3.org/1999/xhtml"
37 xmlns:svg="http://www.w3.org/2000/svg"
38 xmlns:ns="http://docbook.org/ns/docbook"
39 xmlns:mathml="http://www.w3.org/1998/Math/MathML">
40 <title>Standard View Helper Library</title>
41
42 <para>Should be autogenerated from the tags.</para>
43 </section>');
44 foreach ($classNames as $className) {
45 $this->generateXmlForClassName($className, $namespace, $xmlRootNode);
46 }
47 return $xmlRootNode->asXML();
48 }
49
50 /**
51 * Generate the XML Schema for a given class name.
52 *
53 * @param string $className Class name to generate the schema for.
54 * @param string $namespace Namespace prefix. Used to split off the first parts of the class name.
55 * @param \SimpleXMLElement $xmlRootNode XML root node where the xsd:element is appended.
56 * @return void
57 */
58 protected function generateXmlForClassName($className, $namespace, \SimpleXMLElement $xmlRootNode) {
59 $reflectionClass = new \TYPO3\CMS\Extbase\Reflection\ClassReflection($className);
60 if (!$reflectionClass->isSubclassOf($this->abstractViewHelperReflectionClass)) {
61 return;
62 }
63 $tagName = $this->getTagNameForClass($className, $namespace);
64 $docbookSection = $xmlRootNode->addChild('section');
65 $docbookSection->addChild('title', $tagName);
66 $this->docCommentParser->parseDocComment($reflectionClass->getDocComment());
67 $this->addDocumentation($this->docCommentParser->getDescription(), $docbookSection);
68 $argumentsSection = $docbookSection->addChild('section');
69 $argumentsSection->addChild('title', 'Arguments');
70 $this->addArguments($className, $argumentsSection);
71 return $docbookSection;
72 }
73
74 /**
75 * Add attribute descriptions to a given tag.
76 * Initializes the view helper and its arguments, and then reads out the list of arguments.
77 *
78 * @param string $className Class name where to add the attribute descriptions
79 * @param \SimpleXMLElement $docbookSection DocBook section to add the attributes to.
80 * @return void
81 */
82 protected function addArguments($className, \SimpleXMLElement $docbookSection) {
83 $viewHelper = $this->instanciateViewHelper($className);
84 $argumentDefinitions = $viewHelper->prepareArguments();
85 if (count($argumentDefinitions) === 0) {
86 $docbookSection->addChild('para', 'No arguments defined.');
87 return;
88 }
89 $argumentsTable = $docbookSection->addChild('table');
90 $argumentsTable->addChild('title', 'Arguments');
91 $tgroup = $argumentsTable->addChild('tgroup');
92 $tgroup['cols'] = 4;
93 $this->addArgumentTableRow($tgroup->addChild('thead'), 'Name', 'Type', 'Required', 'Description', 'Default');
94 $tbody = $tgroup->addChild('tbody');
95 foreach ($argumentDefinitions as $argumentDefinition) {
96 $this->addArgumentTableRow($tbody, $argumentDefinition->getName(), $argumentDefinition->getType(), $argumentDefinition->isRequired() ? 'yes' : 'no', $argumentDefinition->getDescription(), $argumentDefinition->getDefaultValue());
97 }
98 }
99
100 /**
101 * Instantiate a view helper.
102 *
103 * @param string $className
104 * @return object
105 */
106 protected function instanciateViewHelper($className) {
107 return $this->objectManager->get($className);
108 }
109
110 /**
111 * @param \SimpleXMLElement $parent
112 * @param string $name
113 * @param string $type
114 * @param bool $required
115 * @param string $description
116 * @param string $default
117 * @return void
118 */
119 private function addArgumentTableRow(\SimpleXMLElement $parent, $name, $type, $required, $description, $default) {
120 $row = $parent->addChild('row');
121 $row->addChild('entry', $name);
122 $row->addChild('entry', $type);
123 $row->addChild('entry', $required);
124 $row->addChild('entry', $description);
125 $row->addChild('entry', (string)$default);
126 }
127
128 /**
129 * Add documentation XSD to a given XML node
130 *
131 * As Eclipse renders newlines only on new <xsd:documentation> tags, we wrap every line in a new
132 * <xsd:documentation> tag.
133 * Furthermore, eclipse strips out tags - the only way to prevent this is to have every line wrapped in a
134 * CDATA block AND to replace the < and > with their XML entities. (This is IMHO not XML conformant).
135 *
136 * @param string $documentation Documentation string to add.
137 * @param \SimpleXMLElement $docbookSection Node to add the documentation to
138 * @return void
139 */
140 protected function addDocumentation($documentation, \SimpleXMLElement $docbookSection) {
141 $splitRegex = '/^\\s*(=[^=]+=)$/m';
142 $regex = '/^\\s*(=([^=]+)=)$/m';
143 $matches = preg_split($splitRegex, $documentation, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
144 $currentSection = $docbookSection;
145 foreach ($matches as $singleMatch) {
146 if (preg_match($regex, $singleMatch, $tmp)) {
147 $currentSection = $docbookSection->addChild('section');
148 $currentSection->addChild('title', trim($tmp[2]));
149 } else {
150 $this->addText(trim($singleMatch), $currentSection);
151 }
152 }
153 }
154
155 /**
156 * @param string $text
157 * @param \SimpleXMLElement $parentElement
158 */
159 protected function addText($text, \SimpleXMLElement $parentElement) {
160 $splitRegex = '/
161 (<code(?:.*?)>
162 (?:.*?)
163 <\\/code>)/xs';
164 $regex = '/
165 <code(.*?)>
166 (.*?)
167 <\\/code>/xs';
168 $matches = preg_split($splitRegex, $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
169 foreach ($matches as $singleMatch) {
170 if (preg_match($regex, $singleMatch, $tmp)) {
171 preg_match('/title="([^"]+)"/', $tmp[1], $titleMatch);
172 $example = $parentElement->addChild('example');
173 if (count($titleMatch)) {
174 $example->addChild('title', trim($titleMatch[1]));
175 } else {
176 $example->addChild('title', 'Example');
177 }
178 $this->addChildWithCData($example, 'programlisting', trim($tmp[2]));
179 } else {
180 $textParts = explode("\n", $singleMatch);
181 foreach ($textParts as $text) {
182 if (trim($text) === '') {
183 continue;
184 }
185 $this->addChildWithCData($parentElement, 'para', trim($text));
186 }
187 }
188 }
189 }
190
191 }