@@ -8,7 +8,9 @@ import org.w3c.dom.Document
88import org.w3c.dom.Node
99import org.w3c.dom.NodeList
1010import org.w3c.dom.ls.DOMImplementationLS
11+ import org.w3c.dom.ls.LSParser
1112import org.w3c.dom.ls.LSSerializer
13+ import org.xml.sax.InputSource
1214
1315import javax.xml.parsers.DocumentBuilder
1416import javax.xml.parsers.DocumentBuilderFactory
@@ -21,6 +23,7 @@ import javax.xml.xpath.XPath
2123import javax.xml.xpath.XPathConstants
2224import javax.xml.xpath.XPathFactory
2325import java.util.regex.Matcher
26+ import java.util.regex.Pattern
2427
2528/**
2629 * This task scans the systemd source code man pages to extract the set of available options as well as (eventually) documentation.
@@ -231,6 +234,7 @@ class GenerateDataFromManPages extends DefaultTask {
231234 ' [QuickFairQueueing] Section Options' : [' QuickFairQueueing' ],
232235 ' [QuickFairQueueingClass] Section Options' : [' QuickFairQueueingClass' ],
233236 ' [BridgeVLAN] Section Options' : [' BridgeVLAN' ],
237+
234238 ]
235239 ]
236240 ],
@@ -254,20 +258,6 @@ class GenerateDataFromManPages extends DefaultTask {
254258 @Internal
255259 final DocumentBuilderFactory dbf
256260
257- public GenerateDataFromManPages () {
258- xpath = XPathFactory . newInstance(). newXPath()
259-
260- dbf = DocumentBuilderFactory . newInstance()
261- dbf. setXIncludeAware(true )
262- dbf. setValidating(false )
263- dbf. setExpandEntityReferences(false )
264-
265- dbf. setFeature(" http://apache.org/xml/features/disallow-doctype-decl" , false )
266- dbf. setFeature(" http://apache.org/xml/features/nonvalidating/load-external-dtd" , false )
267- dbf. setFeature(" http://xml.org/sax/features/external-general-entities" , false )
268- dbf. setFeature(" http://xml.org/sax/features/external-parameter-entities" , false )
269- }
270-
271261
272262 @TaskAction
273263 void start () {
@@ -418,6 +408,21 @@ class GenerateDataFromManPages extends DefaultTask {
418408 [name, value]
419409 }
420410
411+ public GenerateDataFromManPages () {
412+ xpath = XPathFactory . newInstance(). newXPath()
413+
414+ dbf = DocumentBuilderFactory . newInstance()
415+ dbf. setXIncludeAware(true )
416+ // dbf.setNamespaceAware(true)
417+ dbf. setValidating(false )
418+ dbf. setExpandEntityReferences(false )
419+
420+ dbf. setFeature(" http://apache.org/xml/features/disallow-doctype-decl" , false )
421+ dbf. setFeature(" http://apache.org/xml/features/nonvalidating/load-external-dtd" , false )
422+ dbf. setFeature(" http://xml.org/sax/features/external-general-entities" , false )
423+ dbf. setFeature(" http://xml.org/sax/features/external-parameter-entities" , false )
424+ }
425+
421426 /**
422427 * Generates individual HTML files for use as inline documentation
423428 *
@@ -429,14 +434,66 @@ class GenerateDataFromManPages extends DefaultTask {
429434 private generateDocumentationHtmlFromManPages (String fileType , File sourceFile ) {
430435 DocumentBuilder builder = dbf. newDocumentBuilder()
431436
432- Document document = builder. parse(sourceFile)
437+ String xmlContent = sourceFile. text
438+
439+
440+ // I spent an hour with ChatGPT trying to get
441+ // Xincludes to work properly (without spending 6 hours to understand them).
442+ // Couldn't get it to work, due to Java not liking things like includes with no href, for local references.
443+ // 💣 Step 1: Replace XInclude elements using regex
444+ xmlContent = processXIncludesWithRegex(xmlContent, sourceFile. parentFile)
445+
446+
447+ File outputDir = project. layout. buildDirectory. dir(" tmp/rendered-xincludes" ). get(). asFile
448+ if (! outputDir. exists()) {
449+ outputDir. mkdirs()
450+ }
451+
452+ File outputFile = new File (outputDir, sourceFile. getName())
453+ outputFile. text = xmlContent // Save to file
454+
455+ Document document = builder. parse(outputFile)
433456 Transformer transformer = getXsltTransformer()
434457
435458 String xsltOutput = transformDocument(document, transformer)
436459
437460 segmentParametersIntoFiles(fileType, sourceFile. getName(), xsltOutput)
438461 }
439462
463+ private String processXIncludesWithRegex (String xmlContent , File baseDir ) {
464+ // 🔥 Regex to match <xi:include href="some.xml" xpointer="some-id"/> (xpointer is optional)
465+ def includePattern = / < xi :include\s+ href= " ([^" ]+ )" (?:\s +xpointer=" ([^" ]+)" )? \s* \/ >/
466+
467+
468+ return xmlContent. replaceAll(includePattern) { match , href , xpointer ->
469+ File includedFile = new File (baseDir, href)
470+
471+ if (! includedFile. exists()) {
472+ println " ⚠️ WARNING: Included file '${ includedFile.absolutePath} ' not found!"
473+ return " <!-- Failed to include: $href -->"
474+ }
475+
476+ String includedContent = includedFile. text
477+
478+ if (xpointer) {
479+ // Extract only the element with the specified ID
480+
481+ // includedContent = extractById(includedContent, xpointer) ?: "<!-- Failed to find xpointer '$xpointer' in $href -->"
482+
483+ def xmlContent2 = includedContent
484+ def idPattern = Pattern . compile(/ <([a-zA-Z0-9:_-]+)\s +[^>]*id=["']$xpointer["'][^>]*>(.*?)<\/\1 >/ , Pattern . DOTALL )
485+ def matcher = xmlContent2 =~ idPattern
486+
487+ def resContent = matcher. find() ? matcher. group(0 ) : null
488+
489+ includedContent = " <!--xi:include='$href ' xpointer='$xpointer '-->" + resContent + " <!-- /xi:include='$href ' xpointer='$xpointer ' -->"
490+ }
491+
492+ return includedContent
493+ }
494+ }
495+
496+
440497 /**
441498 * Transforms the supplied document with the supplied transformer
442499 * @param document - XML Document to transform
0 commit comments