Task 841: .XPL File Format

Task 841: .XPL File Format

The .XPL file format refers to the XML Pipeline Language (XProc) as defined by the W3C specification for XProc: An XML Pipeline Language. This format is used to describe sequences of operations on XML documents. It is fundamentally an XML-based format with the media type application/xproc+xml and the typical file extension .xpl. The following is a comprehensive list of intrinsic properties based on the format's specification, focusing on structural, syntactic, and semantic elements that define its composition and behavior within file systems and processing environments:

  • Namespace: The primary namespace is http://www.w3.org/ns/xproc (prefix p), with supporting namespaces including http://www.w3.org/ns/xproc-step (prefix c) for step input/output and http://www.w3.org/ns/xproc-error (prefix err) for error handling.
  • Version: Specified via the version attribute on top-level elements, typically set to "1.0".
  • Root Elements: One of <p:pipeline>, <p:declare-step>, or <p:library>.
  • Key Structural Elements:
  • <p:input>: Defines input ports (attributes: port, primary, sequence, kind, select).
  • <p:output>: Defines output ports (attributes: port, primary, sequence).
  • <p:option>: Declares configurable options (attributes: name, required, select).
  • <p:variable>: Declares variables (attributes: name, select).
  • <p:with-option>: Sets option values (attributes: name, select, port).
  • <p:with-param>: Passes parameters (attributes: name, select, port).
  • <p:pipe>: Connects ports (attributes: step, port).
  • <p:inline>: Embeds inline XML (attribute: exclude-inline-prefixes).
  • <p:document>: References external documents (attribute: href).
  • <p:data>: Handles non-XML data (attribute: href).
  • <p:empty>: Represents empty input/output.
  • <p:for-each>: Iterates over sequences.
  • <p:viewport>: Processes node subsets (attribute: match).
  • <p:choose>, <p:when>, <p:otherwise>: Conditional branching (attributes: test for <p:when>).
  • <p:try>, <p:catch>: Error handling (attribute: code for <p:catch>).
  • <p:group>: Groups steps.
  • <p:log>: Logs output (attributes: port, href).
  • <p:serialization>: Controls output serialization (attributes: port, plus serialization options like method, encoding).
  • <p:documentation> and <p:pipeinfo>: Metadata and annotations.
  • Core Attributes (applicable across elements):
  • name: Unique identifier (NCName).
  • port: Port name (NCName).
  • primary: Boolean indicating primary port status.
  • sequence: Boolean for sequence handling.
  • kind: "document" or "parameter".
  • xpath-version: "1.0" or "2.0".
  • psvi-required: Boolean for PSVI support.
  • use-when: XPath for conditional inclusion.
  • select: XPath for value selection.
  • match: XSLT pattern for node matching.
  • test: XPath for conditions.
  • href: URI for resources.
  • required: Boolean for mandatory options.
  • type: QName for step types.
  • exclude-inline-prefixes: List of prefixes to exclude.
  • Core Steps (built-in operations): Includes p:identity, p:xslt, p:xinclude, p:load, p:store, p:http-request, and others for transformation, validation, and data manipulation.
  • Data Flow Properties: Ports support sequences; connections via pipes; automatic namespace fix-up; error propagation with dynamic/static distinctions.
  • Serialization Properties: Controlled by options such as method (e.g., "xml", "html"), encoding, indent, omit-xml-declaration.
  • Error Handling Properties: Static errors (e.g., syntax issues) and dynamic errors (e.g., runtime failures), with codes like err:XS0001 for cycles.
  • File System Intrinsic Aspects: As an XML format, it inherits XML properties like UTF-8/UTF-16 encoding, well-formedness requirements, and entity resolution. No binary headers; purely text-based with optional DTD or schema validation.

Two direct download links for example .XPL files (from public GitHub repositories hosting XProc pipelines):

The following is an HTML snippet with embedded JavaScript suitable for embedding in a Ghost blog post (or similar platform). It enables drag-and-drop functionality for a .XPL file, parses it as XML, and dumps the properties (as defined in task 1) to the screen by extracting and displaying key elements, attributes, and structural details.

Drag and drop a .XPL file here
  1. The following is a Python class for handling .XPL files. It uses the xml.etree.ElementTree module to parse (decode/read), extract and print properties, and write a simple example .XPL file.
import xml.etree.ElementTree as ET
import os

class XPLHandler:
    NS = '{http://www.w3.org/ns/xproc}'

    def __init__(self, filepath):
        self.filepath = filepath
        self.tree = None
        self.root = None

    def read(self):
        """Parse and read the .XPL file."""
        self.tree = ET.parse(self.filepath)
        self.root = self.tree.getroot()

    def print_properties(self):
        """Print all properties from the specification list."""
        if not self.root:
            raise ValueError("File not read yet.")
        
        props = {
            'namespace': self.root.nsmap.get('p', 'Not found'),
            'version': self.root.get('version', 'Not specified'),
            'root_element': self.root.tag.replace(self.NS, 'p:'),
            'inputs': [el.get('port') for el in self.root.findall(f".//{self.NS}input")],
            'outputs': [el.get('port') for el in self.root.findall(f".//{self.NS}output")],
            'options': [el.get('name') for el in self.root.findall(f".//{self.NS}option")],
            'variables': [el.get('name') for el in self.root.findall(f".//{self.NS}variable")],
            'steps': [el.get('type') for el in self.root.findall(f".//*[@type]")],
            # Additional properties can be extracted similarly
        }
        for key, value in props.items():
            print(f"{key}: {value}")

    def write_example(self, output_path):
        """Write a simple example .XPL file."""
        root = ET.Element(self.NS + 'pipeline', attrib={'version': '1.0', 'xmlns:p': 'http://www.w3.org/ns/xproc'})
        input_el = ET.SubElement(root, self.NS + 'input', {'port': 'source'})
        output_el = ET.SubElement(root, self.NS + 'output', {'port': 'result'})
        tree = ET.ElementTree(root)
        tree.write(output_path, encoding='utf-8', xml_declaration=True)

# Example usage:
# handler = XPLHandler('example.xpl')
# handler.read()
# handler.print_properties()
# handler.write_example('new_example.xpl')
  1. The following is a Java class for handling .XPL files. It uses javax.xml.parsers for parsing (decode/read), extracting and printing properties, and writing a simple example .XPL file.
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class XPLHandler {
    private static final String NS = "http://www.w3.org/ns/xproc";
    private Document doc;

    public XPLHandler(String filepath) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        this.doc = builder.parse(new File(filepath));
    }

    public void printProperties() {
        Element root = doc.getDocumentElement();
        System.out.println("namespace: " + root.getNamespaceURI());
        System.out.println("version: " + root.getAttribute("version"));
        System.out.println("root_element: " + root.getLocalName());

        List<String> inputs = getAttributeList("input", "port");
        System.out.println("inputs: " + inputs);

        List<String> outputs = getAttributeList("output", "port");
        System.out.println("outputs: " + outputs);

        List<String> options = getAttributeList("option", "name");
        System.out.println("options: " + options);

        List<String> variables = getAttributeList("variable", "name");
        System.out.println("variables: " + variables);

        // Additional properties can be added
    }

    private List<String> getAttributeList(String tag, String attr) {
        List<String> list = new ArrayList<>();
        NodeList nodes = doc.getElementsByTagNameNS(NS, tag);
        for (int i = 0; i < nodes.getLength(); i++) {
            Element el = (Element) nodes.item(i);
            list.add(el.getAttribute(attr));
        }
        return list;
    }

    public void writeExample(String outputPath) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document newDoc = builder.newDocument();
        Element root = newDoc.createElementNS(NS, "p:pipeline");
        root.setAttribute("version", "1.0");
        newDoc.appendChild(root);

        Element input = newDoc.createElementNS(NS, "p:input");
        input.setAttribute("port", "source");
        root.appendChild(input);

        Element output = newDoc.createElementNS(NS, "p:output");
        output.setAttribute("port", "result");
        root.appendChild(output);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.transform(new DOMSource(newDoc), new StreamResult(new File(outputPath)));
    }

    // Example usage:
    // public static void main(String[] args) throws Exception {
    //     XPLHandler handler = new XPLHandler("example.xpl");
    //     handler.printProperties();
    //     handler.writeExample("new_example.xpl");
    // }
}
  1. The following is a JavaScript class for handling .XPL files (browser-compatible, using DOMParser for parsing). It reads via FileReader, extracts and prints properties to console, and writes a simple example as a Blob for download.
class XPLHandler {
  constructor() {
    this.ns = 'http://www.w3.org/ns/xproc';
    this.xmlDoc = null;
  }

  read(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = (e) => {
        const parser = new DOMParser();
        this.xmlDoc = parser.parseFromString(e.target.result, 'application/xml');
        if (this.xmlDoc.getElementsByTagName('parsererror').length > 0) {
          reject(new Error('Invalid XML'));
        } else {
          resolve();
        }
      };
      reader.readAsText(file);
    });
  }

  printProperties() {
    if (!this.xmlDoc) throw new Error('File not read yet.');
    const root = this.xmlDoc.documentElement;
    console.log('namespace:', root.namespaceURI);
    console.log('version:', root.getAttribute('version') || 'Not specified');
    console.log('root_element:', root.localName);

    const inputs = Array.from(this.xmlDoc.getElementsByTagNameNS(this.ns, 'input')).map(el => el.getAttribute('port'));
    console.log('inputs:', inputs);

    const outputs = Array.from(this.xmlDoc.getElementsByTagNameNS(this.ns, 'output')).map(el => el.getAttribute('port'));
    console.log('outputs:', outputs);

    const options = Array.from(this.xmlDoc.getElementsByTagNameNS(this.ns, 'option')).map(el => el.getAttribute('name'));
    console.log('options:', options);

    const variables = Array.from(this.xmlDoc.getElementsByTagNameNS(this.ns, 'variable')).map(el => el.getAttribute('name'));
    console.log('variables:', variables);

    // Additional properties can be logged similarly
  }

  writeExample() {
    const xml = new DOMParser().parseFromString('<p:pipeline xmlns:p="http://www.w3.org/ns/xproc" version="1.0"></p:pipeline>', 'application/xml');
    const input = xml.createElementNS(this.ns, 'p:input');
    input.setAttribute('port', 'source');
    xml.documentElement.appendChild(input);

    const output = xml.createElementNS(this.ns, 'p:output');
    output.setAttribute('port', 'result');
    xml.documentElement.appendChild(output);

    const serializer = new XMLSerializer();
    const xmlStr = serializer.serializeToString(xml);
    const blob = new Blob([xmlStr], { type: 'application/xml' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'new_example.xpl';
    a.click();
    URL.revokeObjectURL(url);
  }
}

// Example usage:
// const handler = new XPLHandler();
// handler.read(selectedFile).then(() => handler.printProperties());
// handler.writeExample();
  1. The following is a C++ class for handling .XPL files. It uses the TinyXML2 library (assumed available; include via #include <tinyxml2.h>) for parsing, extracting and printing properties, and writing a simple example .XPL file. Note: Compile with TinyXML2 linked.
#include <tinyxml2.h>
#include <iostream>
#include <string>
#include <vector>

class XPLHandler {
private:
    tinyxml2::XMLDocument doc;
    const char* ns = "http://www.w3.org/ns/xproc";  // Not directly used in TinyXML2, but for reference

public:
    XPLHandler(const std::string& filepath) {
        doc.LoadFile(filepath.c_str());
    }

    void printProperties() {
        tinyxml2::XMLElement* root = doc.RootElement();
        if (!root) {
            std::cerr << "Error: Invalid XML." << std::endl;
            return;
        }

        std::cout << "namespace: " << (root->Namespace() ? root->Namespace() : "Not found") << std::endl;
        std::cout << "version: " << (root->Attribute("version") ? root->Attribute("version") : "Not specified") << std::endl;
        std::cout << "root_element: " << root->Name() << std::endl;

        std::vector<std::string> inputs;
        for (tinyxml2::XMLElement* el = root->FirstChildElement("input"); el; el = el->NextSiblingElement("input")) {
            inputs.push_back(el->Attribute("port") ? el->Attribute("port") : "");
        }
        std::cout << "inputs: ";
        for (const auto& val : inputs) std::cout << val << " ";
        std::cout << std::endl;

        std::vector<std::string> outputs;
        for (tinyxml2::XMLElement* el = root->FirstChildElement("output"); el; el = el->NextSiblingElement("output")) {
            outputs.push_back(el->Attribute("port") ? el->Attribute("port") : "");
        }
        std::cout << "outputs: ";
        for (const auto& val : outputs) std::cout << val << " ";
        std::cout << std::endl;

        // Similar for options, variables, etc.
    }

    void writeExample(const std::string& outputPath) {
        tinyxml2::XMLDocument newDoc;
        tinyxml2::XMLElement* root = newDoc.NewElement("p:pipeline");
        root->SetAttribute("xmlns:p", ns);
        root->SetAttribute("version", "1.0");
        newDoc.InsertFirstChild(root);

        tinyxml2::XMLElement* input = newDoc.NewElement("p:input");
        input->SetAttribute("port", "source");
        root->InsertEndChild(input);

        tinyxml2::XMLElement* output = newDoc.NewElement("p:output");
        output->SetAttribute("port", "result");
        root->InsertEndChild(output);

        newDoc.SaveFile(outputPath.c_str());
    }
};

// Example usage:
// int main() {
//     XPLHandler handler("example.xpl");
//     handler.printProperties();
//     handler.writeExample("new_example.xpl");
//     return 0;
// }