From e44d8a6541e5a7d85c0784d2bdb06ef60aed14ad Mon Sep 17 00:00:00 2001 From: Svante Schubert Date: Tue, 29 Sep 2026 12:39:56 +0200 Subject: [PATCH 1/2] Replace java-rdfa by ODF-specific in-content metadata extraction (Refs #445) ODF uses only a self-contained subset of RDFa, so InContentMetadata now builds the per-element RDF cache lazily from the DOM instead of running the unmaintained java-rdfa parser on every XML load. This drops java-rdfa, its legacy jena-iri pin and commons-validator. The internal RDFa plumbing classes of org.odftoolkit.odfdom.pkg.rdfa are removed; the Jena model API is unchanged. --- .github/workflows/deployment.yml | 6 +- .github/workflows/maven.yml | 8 +- README.md | 3 +- odfdom/pom.xml | 8 +- odfdom/src/main/java/module-info.java | 3 +- .../rdfa/BookmarkRDFMetadataExtractor.java | 68 +-- .../org/odftoolkit/odfdom/pkg/OdfFileDom.java | 68 ++- .../odfdom/pkg/OdfFileSaxHandler.java | 23 - .../odfdom/pkg/rdfa/DOMAttributes.java | 93 ---- .../odfdom/pkg/rdfa/DOMRDFaParser.java | 99 ----- .../odfdom/pkg/rdfa/EvalContext.java | 203 --------- .../odfdom/pkg/rdfa/InContentMetadata.java | 201 +++++++++ .../odftoolkit/odfdom/pkg/rdfa/JenaSink.java | 157 ------- .../odfdom/pkg/rdfa/MultiContentHandler.java | 108 ----- .../odfdom/pkg/rdfa/RDFaParser.java | 413 ------------------ .../odfdom/pkg/rdfa/SAXRDFaParser.java | 144 ------ .../odfdom/pkg/rdfa/URIExtractor.java | 75 ---- .../odfdom/pkg/rdfa/URIExtractorImpl.java | 202 --------- .../odfdom/pkg/rdfa/package-info.java | 88 ++++ .../odfdom/pkg/RDFMetadataTest.java | 105 +++++ pom.xml | 25 +- 21 files changed, 455 insertions(+), 1645 deletions(-) delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMAttributes.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMRDFaParser.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/EvalContext.java create mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/InContentMetadata.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/JenaSink.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/MultiContentHandler.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/RDFaParser.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/SAXRDFaParser.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractor.java delete mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractorImpl.java create mode 100644 odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/package-info.java diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 0a5c9448ee..6827624ebf 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '21' distribution: 'temurin' cache: maven @@ -48,7 +48,7 @@ jobs: file: target/release/**/*.* file_glob: true overwrite: true - body: "Support of **ODF 1.2** and >=**JDK 17**\n + body: "Support of **ODF 1.2** and >=**JDK 21**\n \n Detailed documentation:\n https://tdf.github.io/odftoolkit/ReleaseNotes.html#release-${{ env.release_version }}\n diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 720413df8b..c3d46fc9c0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '21' distribution: 'temurin' cache: maven - name: Build with Maven @@ -43,10 +43,10 @@ jobs: with: # shallow clones should be disabled for a better relevancy of the analysis fetch-depth: 0 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: - java-version: '17' + java-version: '21' distribution: 'temurin' cache: maven - name: Cache SonarQube packages diff --git a/README.md b/README.md index 0922e09638..ee8535c5cd 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ People interested should follow the [mail list](https://tdf.github.io/odftoolkit ## Getting Started -The ODF Toolkit is based on Java (tested with JDK 11) and uses the Maven 3 +The current ODF Toolkit development version (0.14.0-SNAPSHOT) requires JDK 21 or later +to build and run, and uses Apache Jena 6.2.0. It uses the Maven 3 build system. To build ODF Toolkit, use the following command in this directory: mvn clean install diff --git a/odfdom/pom.xml b/odfdom/pom.xml index 6319dab55d..d56d0704e5 100644 --- a/odfdom/pom.xml +++ b/odfdom/pom.xml @@ -65,12 +65,8 @@ jena-core - net.rootdev - java-rdfa - - - commons-validator - commons-validator + org.apache.jena + jena-arq diff --git a/odfdom/src/main/java/module-info.java b/odfdom/src/main/java/module-info.java index 5b62f7e492..2e73738bc8 100644 --- a/odfdom/src/main/java/module-info.java +++ b/odfdom/src/main/java/module-info.java @@ -82,12 +82,11 @@ requires java.desktop; requires java.logging; - requires java.rdfa; requires java.xml; - requires org.apache.commons.validator; requires org.apache.commons.compress; requires org.apache.commons.lang3; requires org.apache.jena.core; + requires org.apache.jena.arq; requires org.bouncycastle.provider; requires org.json; requires org.slf4j; diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/dom/rdfa/BookmarkRDFMetadataExtractor.java b/odfdom/src/main/java/org/odftoolkit/odfdom/dom/rdfa/BookmarkRDFMetadataExtractor.java index 9f70e88caf..4d040c9af8 100644 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/dom/rdfa/BookmarkRDFMetadataExtractor.java +++ b/odfdom/src/main/java/org/odftoolkit/odfdom/dom/rdfa/BookmarkRDFMetadataExtractor.java @@ -24,28 +24,18 @@ package org.odftoolkit.odfdom.dom.rdfa; import java.util.HashMap; -import java.util.Iterator; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Map.Entry; -import javax.xml.stream.XMLEventFactory; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; import org.odftoolkit.odfdom.dom.DefaultElementVisitor; import org.odftoolkit.odfdom.dom.OdfDocumentNamespace; import org.odftoolkit.odfdom.dom.element.text.TextBookmarkEndElement; import org.odftoolkit.odfdom.dom.element.text.TextBookmarkStartElement; import org.odftoolkit.odfdom.pkg.OdfElement; import org.odftoolkit.odfdom.pkg.OdfFileDom; -import org.odftoolkit.odfdom.pkg.rdfa.DOMAttributes; -import org.odftoolkit.odfdom.pkg.rdfa.JenaSink; +import org.odftoolkit.odfdom.pkg.rdfa.InContentMetadata; import org.w3c.dom.Node; -import org.xml.sax.Attributes; /** * This is a sub class of DefaultElementVisitor, which is used to extract metadata from @@ -60,10 +50,6 @@ public class BookmarkRDFMetadataExtractor extends DefaultElementVisitor { protected final Map builderMap; protected final Map stringMap; - private XMLEventFactory eventFactory = XMLEventFactory.newInstance(); - - private JenaSink sink; - /** * This class is used to provide the string builder functions to extractor. It will automatically * process the last NewLineChar. @@ -136,7 +122,6 @@ public static BookmarkRDFMetadataExtractor newBookmarkTextExtractor() { public Model getBookmarkRDFMetadata(OdfFileDom dom) { this.bookmarkstart = null; this.found = false; - this.sink = dom.getSink(); visit(dom.getRootElement()); return getModel(); } @@ -144,62 +129,23 @@ public Model getBookmarkRDFMetadata(OdfFileDom dom) { public Model getBookmarkRDFMetadata(TextBookmarkStartElement bookmarkstart) { this.bookmarkstart = bookmarkstart; this.found = false; - this.sink = ((OdfFileDom) bookmarkstart.getOwnerDocument()).getSink(); visit(((OdfFileDom) bookmarkstart.getOwnerDocument()).getRootElement()); return getModel(); } + /** + * Creates the statements of all collected bookmarks, using the text between each + * <text:bookmark-start> and its <text:bookmark-end> as literal + * content, see {@link InContentMetadata}. + */ private Model getModel() { Model m = ModelFactory.createDefaultModel(); for (Entry entry : stringMap.entrySet()) { - String xhtmlAbout = entry.getKey().getXhtmlAboutAttribute(); - String xhtmlProperty = entry.getKey().getXhtmlPropertyAttribute(); - String xhtmlContent = entry.getKey().getXhtmlContentAttribute(); - if (xhtmlAbout != null && xhtmlProperty != null) { - String qname = entry.getKey().getNodeName(); - String namespaceURI = entry.getKey().getNamespaceURI(); - String localname = entry.getKey().getLocalName(); - String prefix = (qname.indexOf(':') == -1) ? "" : qname.substring(0, qname.indexOf(':')); - - StartElement e = - eventFactory.createStartElement( - prefix, - namespaceURI, - localname, - fromAttributes(new DOMAttributes(entry.getKey().getAttributes())), - null, - sink.getContext()); - - xhtmlAbout = sink.getExtractor().expandSafeCURIE(e, xhtmlAbout, sink.getContext()); - xhtmlProperty = sink.getExtractor().expandCURIE(e, xhtmlProperty, sink.getContext()); - Resource s = m.createResource(xhtmlAbout); - Property p = m.createProperty(xhtmlProperty); - if (xhtmlContent != null) { - s.addLiteral(p, xhtmlContent); - } else { - s.addLiteral(p, entry.getValue()); - } - } + InContentMetadata.addStatements(m, entry.getKey(), entry.getValue()); } return m; } - private Iterator fromAttributes(Attributes attributes) { - List toReturn = new ArrayList<>(); - - for (int i = 0; i < attributes.getLength(); i++) { - String qname = attributes.getQName(i); - String prefix = qname.contains(":") ? qname.substring(0, qname.indexOf(":")) : ""; - Attribute attr = - eventFactory.createAttribute( - prefix, attributes.getURI(i), attributes.getLocalName(i), attributes.getValue(i)); - - if (!qname.equals("xmlns") && !qname.startsWith("xmlns:")) toReturn.add(attr); - } - - return toReturn.iterator(); - } - /** * Constructor with an ODF element as parameter * diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileDom.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileDom.java index 986bb7f5ae..30de81b6da 100644 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileDom.java +++ b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileDom.java @@ -48,10 +48,7 @@ import org.odftoolkit.odfdom.dom.OdfStylesDom; import org.odftoolkit.odfdom.dom.rdfa.BookmarkRDFMetadataExtractor; import org.odftoolkit.odfdom.pkg.manifest.OdfManifestDom; -import org.odftoolkit.odfdom.pkg.rdfa.DOMRDFaParser; -import org.odftoolkit.odfdom.pkg.rdfa.JenaSink; -import org.odftoolkit.odfdom.pkg.rdfa.MultiContentHandler; -import org.odftoolkit.odfdom.pkg.rdfa.SAXRDFaParser; +import org.odftoolkit.odfdom.pkg.rdfa.InContentMetadata; import org.odftoolkit.odfdom.pkg.rdfa.Util; import org.w3c.dom.DOMException; import org.w3c.dom.Document; @@ -76,13 +73,12 @@ public class OdfFileDom extends DocumentImpl implements NamespaceContext { /** Contains only the duplicate prefix. The primary hold by mPrefixByUri still have to be added */ protected Map> mDuplicatePrefixesByUri; /** - * The cache of in content metadata: key: a Node in the dom ; value: the Jena RDF model of triples - * of the Node + * The cache of in content metadata: key: an element of this DOM carrying RDFa attributes; value: + * the Jena RDF model of its triples. It remains null until first requested by {@link + * #getInContentMetadataCache()}, see {@link org.odftoolkit.odfdom.pkg.rdfa}. */ protected Map inCententMetadataCache; - protected JenaSink sink; - /** * Creates the DOM representation of an XML file of an ODF document. * @@ -98,7 +94,6 @@ protected OdfFileDom(OdfPackageDocument packageDocument, String packagePath) { mUriByPrefix = new HashMap<>(); mPrefixByUri = new HashMap<>(); mDuplicatePrefixesByUri = new HashMap<>(); - inCententMetadataCache = new IdentityHashMap<>(); try { initialize(); } catch (SAXException | IOException | ParserConfigurationException ex) { @@ -128,7 +123,6 @@ protected OdfFileDom(OdfPackage pkg, String packagePath) { mUriByPrefix = new HashMap<>(); mPrefixByUri = new HashMap<>(); mDuplicatePrefixesByUri = new HashMap<>(); - inCententMetadataCache = new HashMap<>(); try { initialize(); } catch (SAXException | IOException | ParserConfigurationException ex) { @@ -218,19 +212,7 @@ protected void initialize(DefaultHandler handler, OdfFileDom dom) try (InputStream fileStream = mPackage.getInputStream(mPackagePath)) { if (fileStream != null) { XMLReader xmlReader = mPackage.getXMLReader(); - String baseUri = Util.getRDFBaseUri(mPackage.getBaseURI(), mPackagePath); - if (handler instanceof OdfFileSaxHandler) { - OdfFileSaxHandler odfSaxHandler = ((OdfFileSaxHandler) handler); - sink = new JenaSink(this); - odfSaxHandler.setSink(sink); - SAXRDFaParser rdfa = SAXRDFaParser.createInstance(sink); - rdfa.setBase(baseUri); - // the file is parsed by ODF ContentHandler, and then RDFa ContentHandler - MultiContentHandler multi = new MultiContentHandler(odfSaxHandler, rdfa); - xmlReader.setContentHandler(multi); - } else { - xmlReader.setContentHandler(handler); - } + xmlReader.setContentHandler(handler); InputSource xmlSource = new InputSource(fileStream); xmlReader.parse(xmlSource); } @@ -665,26 +647,36 @@ public OdfNamespace setNamespace(NamespaceName name) { } /** - * Get in-content metadata cache model + * Get the in-content metadata cache, mapping each element carrying RDFa attributes to the RDF + * triples it states. + * + *

The cache is built from the DOM on the first call, see {@link + * org.odftoolkit.odfdom.pkg.rdfa}. Afterwards it is kept up to date by {@link + * #updateInContentMetadataCache(Node)}. * * @return in-content metadata cache model */ public Map getInContentMetadataCache() { - return this.inCententMetadataCache; + if (inCententMetadataCache == null) { + inCententMetadataCache = new IdentityHashMap<>(); + InContentMetadata.collect(getRootElement(), inCententMetadataCache); + } + return inCententMetadataCache; } /** - * Update the in content metadata of the node. It should be called whenever the xhtml:xxx - * attributes values of the node are changed. + * Update the in content metadata of the node and all its descendants. It should be called + * whenever the xhtml:xxx attributes values or the text content of the node are changed. * - * @param the node, whose in content metadata will be updated + *

Before the cache has been built, there is nothing to update, as the cache will be created + * from the current DOM when first requested. + * + * @param node the node, whose in content metadata will be updated */ public void updateInContentMetadataCache(Node node) { - this.getInContentMetadataCache().remove(node); - DOMRDFaParser parser = DOMRDFaParser.createInstance(this.sink); - String baseUri = Util.getRDFBaseUri(mPackage.getBaseURI(), mPackagePath); - parser.setBase(baseUri); - parser.parse(node); + if (inCententMetadataCache != null) { + InContentMetadata.collect(node, inCententMetadataCache); + } } /** @return the RDF metadata of all the bookmarks within the dom */ @@ -693,12 +685,14 @@ public Model getBookmarkRDFMetadata() { } /** - * The end users needn't to care of this method, which is used by BookmarkRDFMetadataExtractor + * Get the base IRI for the RDF metadata of this XML file: the IRI of the directory containing the + * file within the package, always ending with a slash, e.g. file:///docs/test.odt/. + * An empty xhtml:about attribute refers to this IRI. * - * @return the JenaSink + * @return the base IRI for RDF metadata of this XML file */ - public JenaSink getSink() { - return sink; + public String getRDFBaseUri() { + return Util.getRDFBaseUri(mPackage.getBaseURI(), mPackagePath); } /** diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileSaxHandler.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileSaxHandler.java index ee0a25d90c..bc8ade0421 100644 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileSaxHandler.java +++ b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfFileSaxHandler.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.util.Objects; import java.util.Stack; -import org.odftoolkit.odfdom.pkg.rdfa.JenaSink; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; @@ -47,7 +46,6 @@ public class OdfFileSaxHandler extends DefaultHandler { // they are required and must pop themselves from the stack when done private Stack mHandlerStack = new Stack<>(); private StringBuilder mCharsForTextNode = new StringBuilder(); - private JenaSink sink; public OdfFileSaxHandler(Node rootNode) throws SAXException { if (rootNode instanceof OdfFileDom) { @@ -138,9 +136,6 @@ public void startElement(String uri, String localName, String qName, Attributes mCurrentNode.appendChild(element); // push the new element as the context node... mCurrentNode = element; - if (!localName.equals("bookmark-start")) { - setContextNode(mCurrentNode); - } } /** @@ -171,22 +166,4 @@ public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { return super.resolveEntity(publicId, systemId); } - - /** - * Expose the current node to JenaSink to for caching the parsed RDF triples. - */ - protected void setContextNode(Node node) { - if (this.sink != null) { - sink.setContextNode(node); - } - } - - /** - * Set the JenaSink object. - * - * @param sink - */ - public void setSink(JenaSink sink) { - this.sink = sink; - } } diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMAttributes.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMAttributes.java deleted file mode 100644 index caead04d05..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMAttributes.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import org.w3c.dom.NamedNodeMap; -import org.xml.sax.Attributes; - -/** Simple wrapper class for NamedNodeMap as Attributes */ -public class DOMAttributes implements Attributes { - - private NamedNodeMap attributes; - - /** - * Class constructor - * - * @param attributes - */ - public DOMAttributes(NamedNodeMap attributes) { - this.attributes = attributes; - } - - public int getLength() { - return attributes.getLength(); - } - - public String getURI(int index) { - return attributes.item(index).getNamespaceURI(); - } - - public String getLocalName(int index) { - return attributes.item(index).getLocalName(); - } - - public String getQName(int index) { - return attributes.item(index).getNodeName(); - } - - public String getType(int index) { - throw new RuntimeException("DOMAttributes.getType() is not supported"); - } - - public String getValue(int index) { - return attributes.item(index).getNodeValue(); - } - - public int getIndex(String uri, String localName) { - throw new RuntimeException( - "DOMAttributes.getIndex(String uri, String localName) is not supported"); - } - - public int getIndex(String qName) { - throw new RuntimeException("DOMAttributes.getIndex(String qName) is not supported"); - } - - public String getType(String uri, String localName) { - throw new RuntimeException( - "DOMAttributes.getType(String uri, String localName) is not supported"); - } - - public String getType(String qName) { - throw new RuntimeException("DOMAttributes.getType(String qName) is not supported"); - } - - public String getValue(String uri, String localName) { - throw new RuntimeException( - "DOMAttributes.getValue(String uri, String localName) is not supported"); - } - - public String getValue(String qName) { - throw new RuntimeException("DOMAttributes.getValue(String qName) is not supported"); - } -} diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMRDFaParser.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMRDFaParser.java deleted file mode 100644 index ec11d4379a..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/DOMRDFaParser.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import javax.xml.stream.XMLEventFactory; -import javax.xml.stream.XMLOutputFactory; -import org.odftoolkit.odfdom.dom.element.text.TextBookmarkStartElement; -import org.w3c.dom.Node; - -/** A RDFa parser for DOM */ -public class DOMRDFaParser extends RDFaParser { - - private static final XMLOutputFactory DEFAULT_XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory(); - private static final XMLEventFactory DEFAULT_XML_EVENT_FACTORY = XMLEventFactory.newFactory(); - - public static DOMRDFaParser createInstance(JenaSink sink) { - sink.getExtractor().setForSAX(false); - return new DOMRDFaParser(sink, sink.getExtractor()); - } - - public DOMRDFaParser( - JenaSink sink, - XMLOutputFactory outputFactory, - XMLEventFactory eventFactory, - URIExtractor extractor) { - super(sink, outputFactory, eventFactory, extractor); - } - - public DOMRDFaParser(JenaSink sink, URIExtractor extractor) { - this(sink, DEFAULT_XML_OUTPUT_FACTORY, DEFAULT_XML_EVENT_FACTORY, extractor); - } - - /** - * Parse the RDFa in-content metadata of the node. - * - * @param node - */ - public void parse(Node node) { - process(node); - } - - private void process(Node node) { - - switch (node.getNodeType()) { - case Node.ELEMENT_NODE: - if (!(node instanceof TextBookmarkStartElement)) { - sink.setContextNode(node); - } - // Start element - beginRDFaElement( - node.getNamespaceURI(), - node.getLocalName(), - node.getNodeName(), - new DOMAttributes(node.getAttributes())); - // Recurse to child - // if (node.hasChildNodes() == true) { - // process(node.getFirstChild()); - // } - if (node.hasChildNodes() == true) { - Node n = node.getFirstChild(); - process(n); - while (n.getNextSibling() != null) { - process(n.getNextSibling()); - n = n.getNextSibling(); - } - } - - // End element - endRDFaElement(node.getNamespaceURI(), node.getLocalName(), node.getNodeName()); - break; - case Node.CDATA_SECTION_NODE: - case Node.TEXT_NODE: - // Text or CDATA - writeCharacters(node.getNodeValue()); - break; - } - } -} diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/EvalContext.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/EvalContext.java deleted file mode 100644 index 96dac6fd18..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/EvalContext.java +++ /dev/null @@ -1,203 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import javax.xml.namespace.NamespaceContext; - -/** EvalContext modified from net.rootdev.javardfa.EvalContext */ -final class EvalContext implements NamespaceContext { - - EvalContext parent; - String base; - String parentSubject; - String parentObject; - String language; - String vocab; - List forwardProperties; - List backwardProperties; - Map xmlnsMap = Collections.emptyMap(); - Map prefixMap = Collections.emptyMap(); - - protected EvalContext(String base) { - super(); - this.base = base; - this.parentSubject = base; - this.forwardProperties = new ArrayList<>(); - this.backwardProperties = new ArrayList<>(); - } - - public EvalContext(EvalContext toCopy) { - super(); - this.base = toCopy.base; - this.parentSubject = toCopy.parentSubject; - this.parentObject = toCopy.parentObject; - this.language = toCopy.language; - this.forwardProperties = new ArrayList<>(toCopy.forwardProperties); - this.backwardProperties = new ArrayList<>(toCopy.backwardProperties); - this.parent = toCopy; - this.vocab = toCopy.vocab; - } - - public void setBase(String abase) { - // This is very dodgy. We want to check if ps and po have been changed - // from their typical values (base). - // Base changing happens very late in the day when we're streaming, and - // it is very fiddly to handle - boolean setPS = Objects.equals(parentSubject, base); - boolean setPO = Objects.equals(parentObject, base); - - if (abase.contains("#")) { - this.base = abase.substring(0, abase.indexOf("#")); - } else { - this.base = abase; - } - - if (setPS) this.parentSubject = base; - if (setPO) this.parentObject = base; - - if (parent != null) { - parent.setBase(base); - } - } - - @Override - public String toString() { - return String.format( - "[\n\tBase: %s\n\tPS: %s\n\tPO: %s\n\tlang: %s\n\tIncomplete: -> %s <- %s\n]", - base, - parentSubject, - parentObject, - language, - forwardProperties.size(), - backwardProperties.size()); - } - - /** - * RDFa 1.1 prefix support - * - * @param prefix Prefix - * @param uri URI - */ - public void setPrefix(String prefix, String uri) { - if (uri.length() == 0) { - uri = base; - } - if (prefixMap == Collections.EMPTY_MAP) prefixMap = new HashMap<>(); - prefixMap.put(prefix, uri); - } - - /** - * RDFa 1.1 prefix support. - * - * @param prefix - * @return - */ - public String getURIForPrefix(String prefix) { - if (prefixMap.containsKey(prefix)) { - return prefixMap.get(prefix); - } else if (xmlnsMap.containsKey(prefix)) { - return xmlnsMap.get(prefix); - } else if (parent != null) { - return parent.getURIForPrefix(prefix); - } else { - return null; - } - } - - // Namespace methods - public void setNamespaceURI(String prefix, String uri) { - if (uri.length() == 0) { - uri = base; - } - if (xmlnsMap == Collections.EMPTY_MAP) xmlnsMap = new HashMap<>(); - xmlnsMap.put(prefix, uri); - } - - public String getNamespaceURI(String prefix) { - if (xmlnsMap.containsKey(prefix)) { - return xmlnsMap.get(prefix); - } else if (parent != null) { - return parent.getNamespaceURI(prefix); - } else { - return null; - } - } - - public String getPrefix(String uri) { - throw new UnsupportedOperationException("Not supported yet."); - } - - public Iterator getPrefixes(String uri) { - throw new UnsupportedOperationException("Not supported yet."); - } - - // I'm not sure about this 1.1 term business. Reuse prefix map - public void setTerm(String term, String uri) { - setPrefix(term + ":", uri); - } - - public String getURIForTerm(String term) { - return getURIForPrefix(term + ":"); - } - - public String getBase() { - return base; - } - - public String getVocab() { - return vocab; - } -} - -/* - * (c) Copyright 2009 University of Bristol All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. 2. Redistributions in - * binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. 3. The name of the author may not - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/InContentMetadata.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/InContentMetadata.java new file mode 100644 index 0000000000..bc220fb0f1 --- /dev/null +++ b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/InContentMetadata.java @@ -0,0 +1,201 @@ +/* + * Copyright 2026 The Document Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.odftoolkit.odfdom.pkg.rdfa; + +import java.util.Map; +import org.apache.jena.datatypes.TypeMapper; +import org.apache.jena.rdf.model.AnonId; +import org.apache.jena.rdf.model.Literal; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; +import org.odftoolkit.odfdom.dom.element.text.TextBookmarkStartElement; +import org.odftoolkit.odfdom.pkg.OdfFileDom; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Reads the RDF statements that ODF in-content metadata attributes state about an element. + * + *

ODF allows RDF metadata on the elements <text:p>, <text:h> + * , <text:meta>, <text:bookmark-start>, + * <table:table-cell> and <table:covered-table-cell> using four + * attributes of the XHTML namespace (ODF 1.2 Part 1, §4.2.1 "In Content Metadata (RDFa)" and + * §19.905 to §19.908). The ODF schema only permits xhtml:about and + * xhtml:property together, so every such element is self-contained and states one RDF + * statement per predicate: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Mapping of ODF attributes to an RDF statement
RDFODF sourceInterpretation
subjectxhtml:about (URIorSafeCURIE)[prefix:reference] is a safe CURIE, expanded as described below. + * [_:label] or _:label is a blank node, identical for equal labels in the + * same ODF document. An empty value refers to the ODF document itself, the {@link + * OdfFileDom#getRDFBaseUri() RDF base IRI}. Any other value is taken as an IRI, unchanged, + * even when relative.
predicatesxhtml:property (CURIEs)A whitespace separated list of CURIEs, each one resulting in a separate statement.
objectxhtml:content (string), or the text of the elementAlways a literal, without surrounding whitespace. Without xhtml:content it + * is the text of all descendant text nodes, as for {@link Node#getTextContent()}. Markup + * within the element does not create an XML literal, as ODF defines the object as the + * "literal content" of the element. Empty literals create no statement.
datatypexhtml:datatype (CURIE)The expanded CURIE is the datatype IRI of the literal, e.g. xsd:date. If + * it is missing, empty or cannot be expanded, the literal has no explicit datatype.
+ * + *

A CURIE (compact URI, RDFa 1.0 §7) prefix:reference is expanded by + * concatenating the namespace IRI bound to prefix by an XML namespace declaration + * with reference. For example, dc:title becomes + * http://purl.org/dc/elements/1.1/title when the file declares + * xmlns:dc="http://purl.org/dc/elements/1.1/". The prefixes are looked up in the {@link + * OdfFileDom} as {@link javax.xml.namespace.NamespaceContext}, as ODFDOM moves all namespace + * declarations of a file to its root element while loading, and because elements are processed + * before they are inserted into the DOM tree. An empty prefix, as in :reference, + * refers to the XHTML vocabulary http://www.w3.org/1999/xhtml/vocab#. A CURIE + * without a colon or with an undeclared prefix is ignored. + * + *

The object of a <text:bookmark-start> is the text up to the matching + * <text:bookmark-end>, which is not part of the bookmark element itself. This + * text is collected by {@link org.odftoolkit.odfdom.dom.rdfa.BookmarkRDFMetadataExtractor}, which + * calls {@link #addStatements(Model, Element, String)} with it. Therefore {@link #collect(Node, + * Map)} skips bookmarks. + */ +public final class InContentMetadata { + + /** Namespace of the ODF in-content metadata attributes, e.g. xhtml:about. */ + private static final String XHTML = "http://www.w3.org/1999/xhtml"; + + /** IRI prefix for CURIEs with an empty prefix, e.g. :next (RDFa 1.0 §7). */ + private static final String XHTML_VOCAB = "http://www.w3.org/1999/xhtml/vocab#"; + + private InContentMetadata() {} + + /** + * Refreshes the cache entries of the given node and of all its descendant elements. + * + *

Every element whose attributes state RDF statements gets a new model containing exactly + * these statements. Every other element loses its entry, so that removed attributes or emptied + * text do not leave stale statements behind. Bookmarks are skipped, see {@link + * InContentMetadata}. + * + *

Note that the object of an element may be the text of its descendants. A text change within + * a descendant, e.g. of a <text:span> within a <text:p>, + * only reaches the cache when collect is called for the ancestor carrying the + * metadata. + * + * @param node the root of the subtree to be refreshed, ignored unless it is an element + * @param cache the cache to be refreshed, keyed by element identity + */ + public static void collect(Node node, Map cache) { + if (node instanceof Element element) { + Model model = null; + if (element.hasAttributeNS(XHTML, "about") + && !(element instanceof TextBookmarkStartElement)) { + model = ModelFactory.createDefaultModel(); + addStatements(model, element, element.getTextContent()); + } + if (model == null || model.isEmpty()) { + cache.remove(element); + } else { + cache.put(element, model); + } + for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling()) { + collect(child, cache); + } + } + } + + /** + * Adds the RDF statements stated by the in-content metadata attributes of an element to a model. + * + *

Nothing is added, if the element lacks xhtml:about or xhtml:property + * , if the subject cannot be determined or if the literal is empty. + * + * @param model the model receiving the statements + * @param element an element of an {@link OdfFileDom}, carrying in-content metadata attributes + * @param text the literal content of the element, used unless xhtml:content exists + */ + public static void addStatements(Model model, Element element, String text) { + String about = attribute(element, "about"); + String properties = attribute(element, "property"); + String content = attribute(element, "content"); + String lexicalForm = (content != null ? content : text == null ? "" : text).trim(); + Resource subject = about == null ? null : subject(model, element, about.trim()); + if (subject == null || properties == null || lexicalForm.isEmpty()) { + return; + } + String datatype = expand(element, attribute(element, "datatype")); + Literal object = + datatype == null + ? model.createLiteral(lexicalForm) + : model.createTypedLiteral( + lexicalForm, TypeMapper.getInstance().getSafeTypeByName(datatype)); + for (String curie : properties.trim().split("\\s+")) { + String predicate = expand(element, curie); + if (predicate != null) { + model.add(subject, model.createProperty(predicate), object); + } + } + } + + /** Returns the subject of an xhtml:about value, or null if it has none. */ + private static Resource subject(Model model, Element element, String about) { + boolean safeCurie = about.startsWith("[") && about.endsWith("]"); + String value = safeCurie ? about.substring(1, about.length() - 1) : about; + String baseUri = ((OdfFileDom) element.getOwnerDocument()).getRDFBaseUri(); + if (value.startsWith("_:")) { + return model.createResource(AnonId.create(baseUri + value)); + } + String iri = safeCurie ? expand(element, value) : value.isEmpty() ? baseUri : value; + return iri == null ? null : model.createResource(iri); + } + + /** Returns the IRI of a CURIE, or null if it is null, has no colon or an unknown prefix. */ + private static String expand(Element element, String curie) { + int colon = curie == null ? -1 : curie.indexOf(':'); + if (colon < 0) { + return null; + } + String prefix = curie.substring(0, colon); + String namespace = + prefix.isEmpty() + ? XHTML_VOCAB + : ((OdfFileDom) element.getOwnerDocument()).getNamespaceURI(prefix); + return namespace.isEmpty() ? null : namespace + curie.substring(colon + 1); + } + + /** Returns the value of an attribute of the XHTML namespace, or null if it is missing. */ + private static String attribute(Element element, String localName) { + return element.hasAttributeNS(XHTML, localName) + ? element.getAttributeNS(XHTML, localName) + : null; + } +} diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/JenaSink.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/JenaSink.java deleted file mode 100644 index aba9a50c0c..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/JenaSink.java +++ /dev/null @@ -1,157 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.HashMap; -import java.util.Map; -import net.rootdev.javardfa.StatementSink; -import org.apache.jena.rdf.model.Literal; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.rdf.model.Property; -import org.apache.jena.rdf.model.Resource; -import org.odftoolkit.odfdom.pkg.OdfFileDom; -import org.w3c.dom.Node; - -/** To cache the Jena RDF triples parsed from RDFaParser */ -public class JenaSink implements StatementSink { - - // private OdfFileSaxHandler odf; - private Node contextNode; - private OdfFileDom mFileDom; - private Map bnodeLookup; - private URIExtractor extractor; - private EvalContext context; - - public JenaSink(OdfFileDom mFileDom) { - this.mFileDom = mFileDom; - this.bnodeLookup = new HashMap<>(); - } - - // @Override - public void start() { - bnodeLookup = new HashMap<>(); - } - - // @Override - public void end() { - bnodeLookup = null; - } - - // @Override - public void addObject(String subject, String predicate, String object) { - Model model = getContextModel(); - Resource s = getResource(model, subject.trim()); - Property p = model.createProperty(predicate.trim()); - Resource o = getResource(model, object.trim()); - model.add(s, p, o); - } - - // @Override - public void addLiteral( - String subject, String predicate, String lex, String lang, String datatype) { - if (lex.isEmpty()) { - return; - } - Model model = getContextModel(); - Resource s = getResource(model, subject.trim()); - Property p = model.createProperty(predicate.trim()); - Literal o; - if (lang == null && datatype == null) { - o = model.createLiteral(lex.trim()); - } else if (lang != null) { - o = model.createLiteral(lex.trim(), lang.trim()); - } else { - o = model.createTypedLiteral(lex.trim(), datatype.trim()); - } - model.add(s, p, o); - } - - private Resource getResource(Model model, String res) { - if (res.startsWith("_:")) { - if (bnodeLookup.containsKey(res)) { - return bnodeLookup.get(res); - } - Resource bnode = model.createResource(); - bnodeLookup.put(res, bnode); - return bnode; - } else { - return model.createResource(res); - } - } - - public void addPrefix(String prefix, String uri) { - // Model model =getContextModel(); - // try { - // model.setNsPrefix(prefix.trim(), uri.trim()); - // } catch (IllegalPrefixException e) { - // } - } - - public void setBase(String base) {} - - private Model getContextModel() { - Map cache = this.mFileDom.getInContentMetadataCache(); - Model model = cache.get(contextNode); - if (model == null) { - model = ModelFactory.createDefaultModel(); - this.mFileDom.getInContentMetadataCache().put(contextNode, model); - } - return model; - } - - public Node getContextNode() { - return contextNode; - } - - public void setContextNode(Node contextNode) { - this.contextNode = contextNode; - } - - public URIExtractor getExtractor() { - return extractor; - } - - public void setExtractor(URIExtractor extractor) { - this.extractor = extractor; - } - - public EvalContext getContext() { - return context; - } - - public void setContext(EvalContext context) { - this.context = context; - } - - // // Namespace methods - // public void setNamespaceURI(String prefix, String uri) { - // if (uri.length() == 0) { - // uri = base; - // } - // if (xmlnsMap == Collections.EMPTY_MAP) xmlnsMap = new HashMap(); - // xmlnsMap.put(prefix, uri); - // } - -} diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/MultiContentHandler.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/MultiContentHandler.java deleted file mode 100644 index 257c307e89..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/MultiContentHandler.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.ArrayList; -import java.util.Arrays; - -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; - -/** A proxy for delegating the parsing events to its sub ContentHandler(s). */ -public class MultiContentHandler implements ContentHandler { - ArrayList subContentHandlers; - - public MultiContentHandler(ContentHandler... subs) { - subContentHandlers = new ArrayList<>(Arrays.asList(subs)); - } - - public void setDocumentLocator(Locator locator) { - for (ContentHandler sub : subContentHandlers) { - sub.setDocumentLocator(locator); - } - } - - public void startDocument() throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.startDocument(); - } - } - - public void endDocument() throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.endDocument(); - } - } - - public void startPrefixMapping(String prefix, String uri) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.startPrefixMapping(prefix, uri); - } - } - - public void endPrefixMapping(String prefix) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.endPrefixMapping(prefix); - } - } - - public void startElement(String uri, String localName, String qName, Attributes atts) - throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.startElement(uri, localName, qName, atts); - } - } - - public void endElement(String uri, String localName, String qName) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.endElement(uri, localName, qName); - } - } - - public void characters(char[] ch, int start, int length) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.characters(ch, start, length); - } - } - - public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.ignorableWhitespace(ch, start, length); - } - } - - public void processingInstruction(String target, String data) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.processingInstruction(target, data); - } - } - - public void skippedEntity(String name) throws SAXException { - for (ContentHandler sub : subContentHandlers) { - sub.skippedEntity(name); - } - } -} diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/RDFaParser.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/RDFaParser.java deleted file mode 100644 index 31000086b7..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/RDFaParser.java +++ /dev/null @@ -1,413 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.EnumSet; -import java.util.Iterator; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventFactory; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import net.rootdev.javardfa.Constants; -import net.rootdev.javardfa.Setting; -import net.rootdev.javardfa.literal.LiteralCollector; -import net.rootdev.javardfa.uri.IRIResolver; -import net.rootdev.javardfa.uri.URIExtractor10; -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -/** A RDFa Parser modified from net.rootdev.javardfa.Parser */ -class RDFaParser extends net.rootdev.javardfa.Parser { - - boolean ignore = false; - - protected XMLEventFactory eventFactory; - protected JenaSink sink; - protected Set settings; - protected LiteralCollector literalCollector; - protected URIExtractor extractor; - protected Locator locator; - protected EvalContext context; - - protected RDFaParser( - JenaSink sink, - XMLOutputFactory outputFactory, - XMLEventFactory eventFactory, - URIExtractor extractor) { - super(sink, outputFactory, eventFactory, new URIExtractor10(new IRIResolver())); - this.sink = sink; - this.eventFactory = eventFactory; - this.settings = EnumSet.noneOf(Setting.class); - this.extractor = extractor; - - this.literalCollector = new LiteralCollector(this, eventFactory, outputFactory); - - extractor.setSettings(settings); - - // Important, although I guess the caller doesn't get total control - outputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); - } - - protected void beginRDFaElement(String arg0, String localname, String qname, Attributes arg3) { - if (localname.equals("bookmark-start")) { - ignore = true; - return; - } - try { - // System.err.println("Start element: " + arg0 + " " + arg1 + " " + - // arg2); - - // This is set very late in some html5 cases (not even ready by - // document start) - if (context == null) { - this.setBase(locator.getSystemId()); - } - - // Dammit, not quite the same as XMLEventFactory - String prefix = /* (localname.equals(qname)) */ - (qname.indexOf(':') == -1) ? "" : qname.substring(0, qname.indexOf(':')); - if (settings.contains(Setting.ManualNamespaces)) { - getNamespaces(arg3); - if (prefix.length() != 0) { - arg0 = context.getNamespaceURI(prefix); - localname = localname.substring(prefix.length() + 1); - } - } - StartElement e = - eventFactory.createStartElement( - prefix, arg0, localname, fromAttributes(arg3), null, context); - - if (literalCollector.isCollecting()) literalCollector.handleEvent(e); - - // If we are gathering XML we stop parsing - if (!literalCollector.isCollectingXML()) context = parse(context, e); - } catch (XMLStreamException ex) { - throw new RuntimeException("Streaming issue", ex); - } - } - - protected void endRDFaElement(String arg0, String localname, String qname) { - if (localname.equals("bookmark-start")) { - ignore = false; - return; - } - if (literalCollector.isCollecting()) { - String prefix = (localname.equals(qname)) ? "" : qname.substring(0, qname.indexOf(':')); - XMLEvent e = eventFactory.createEndElement(prefix, arg0, localname); - literalCollector.handleEvent(e); - } - // If we aren't collecting an XML literal keep parsing - if (!literalCollector.isCollectingXML()) context = context.parent; - } - - protected void writeCharacters(String value) { - if (!ignore) { - if (literalCollector.isCollecting()) { - XMLEvent e = eventFactory.createCharacters(value); - literalCollector.handleEvent(e); - } - } - } - - /** Set the base uri of the DOM. */ - public void setBase(String base) { - this.context = new EvalContext(base); - sink.setBase(context.getBase()); - } - - protected EvalContext parse(EvalContext context, StartElement element) throws XMLStreamException { - boolean skipElement = false; - String newSubject = null; - String currentObject = null; - List forwardProperties = new ArrayList<>(); - List backwardProperties = new ArrayList<>(); - String currentLanguage = context.language; - - if (settings.contains(Setting.OnePointOne)) { - - if (getAttributeByName(element, Constants.vocab) != null) { - context.vocab = getAttributeByName(element, Constants.vocab).getValue().trim(); - } - - if (getAttributeByName(element, Constants.prefix) != null) { - parsePrefixes(getAttributeByName(element, Constants.prefix).getValue(), context); - } - } - - // The xml / html namespace matching is a bit ropey. I wonder if the - // html 5 - // parser has a setting for this? - if (settings.contains(Setting.ManualNamespaces)) { - if (getAttributeByName(element, Constants.xmllang) != null) { - currentLanguage = getAttributeByName(element, Constants.xmllang).getValue(); - if (currentLanguage.length() == 0) currentLanguage = null; - } else if (getAttributeByName(element, Constants.lang) != null) { - currentLanguage = getAttributeByName(element, Constants.lang).getValue(); - if (currentLanguage.length() == 0) currentLanguage = null; - } - } else if (getAttributeByName(element, Constants.xmllangNS) != null) { - currentLanguage = getAttributeByName(element, Constants.xmllangNS).getValue(); - if (currentLanguage.length() == 0) currentLanguage = null; - } - - if (Constants.base.equals(element.getName()) - && getAttributeByName(element, Constants.href) != null) { - context.setBase(getAttributeByName(element, Constants.href).getValue()); - sink.setBase(context.getBase()); - } - if (getAttributeByName(element, Constants.rev) == null - && getAttributeByName(element, Constants.rel) == null) { - Attribute nSubj = findAttribute(element, Constants.about); - if (nSubj != null) { - newSubject = extractor.getURI(element, nSubj, context); - } - if (newSubject == null) { - if (Constants.body.equals(element.getName()) || Constants.head.equals(element.getName())) { - newSubject = context.base; - } else if (getAttributeByName(element, Constants.typeof) != null) { - newSubject = createBNode(); - } else { - if (context.parentObject != null) { - newSubject = context.parentObject; - } - if (getAttributeByName(element, Constants.property) == null) { - skipElement = true; - } - } - } - } else { - Attribute nSubj = findAttribute(element, Constants.about, Constants.src); - if (nSubj != null) { - newSubject = extractor.getURI(element, nSubj, context); - } - if (newSubject == null) { - // if element is head or body assume about="" - if (Constants.head.equals(element.getName()) || Constants.body.equals(element.getName())) { - newSubject = context.base; - } else if (getAttributeByName(element, Constants.typeof) != null) { - newSubject = createBNode(); - } else if (context.parentObject != null) { - newSubject = context.parentObject; - } - } - Attribute cObj = findAttribute(element, Constants.resource, Constants.href); - if (cObj != null) { - currentObject = extractor.getURI(element, cObj, context); - } - } - - if (newSubject != null && getAttributeByName(element, Constants.typeof) != null) { - List types = - extractor.getURIs(element, getAttributeByName(element, Constants.typeof), context); - for (String type : types) { - emitTriples(newSubject, Constants.rdfType, type); - } - } - - if (currentObject != null) { - if (getAttributeByName(element, Constants.rel) != null) { - emitTriples( - newSubject, - extractor.getURIs(element, getAttributeByName(element, Constants.rel), context), - currentObject); - } - if (getAttributeByName(element, Constants.rev) != null) { - emitTriples( - currentObject, - extractor.getURIs(element, getAttributeByName(element, Constants.rev), context), - newSubject); - } - } else { - if (getAttributeByName(element, Constants.rel) != null) { - forwardProperties.addAll( - extractor.getURIs(element, getAttributeByName(element, Constants.rel), context)); - } - if (getAttributeByName(element, Constants.rev) != null) { - backwardProperties.addAll( - extractor.getURIs(element, getAttributeByName(element, Constants.rev), context)); - } - if (!forwardProperties.isEmpty() || !backwardProperties.isEmpty()) { - // if predicate present - currentObject = createBNode(); - } - } - - // Getting literal values. Complicated! - if (getAttributeByName(element, Constants.property) != null) { - List props = - extractor.getURIs(element, getAttributeByName(element, Constants.property), context); - String dt = getDatatype(element); - if (getAttributeByName(element, Constants.content) != null) { // The - // easy - // bit - String lex = getAttributeByName(element, Constants.content).getValue(); - if (dt == null || dt.length() == 0) { - emitTriplesPlainLiteral(newSubject, props, lex, currentLanguage); - } else { - emitTriplesDatatypeLiteral(newSubject, props, lex, dt); - } - } else { - literalCollector.collect(newSubject, props, dt, currentLanguage); - } - } - - if (!skipElement && newSubject != null) { - emitTriples(context.parentSubject, context.forwardProperties, newSubject); - - emitTriples(newSubject, context.backwardProperties, context.parentSubject); - } - - EvalContext ec = new EvalContext(context); - if (skipElement) { - ec.language = currentLanguage; - } else { - if (newSubject != null) { - ec.parentSubject = newSubject; - } else { - ec.parentSubject = context.parentSubject; - } - - if (currentObject != null) { - ec.parentObject = currentObject; - } else if (newSubject != null) { - ec.parentObject = newSubject; - } else { - ec.parentObject = context.parentSubject; - } - - ec.language = currentLanguage; - ec.forwardProperties = forwardProperties; - ec.backwardProperties = backwardProperties; - } - return ec; - } - - private void getNamespaces(Attributes attrs) { - for (int i = 0; i < attrs.getLength(); i++) { - String qname = attrs.getQName(i); - String prefix = getPrefix(qname); - if ("xmlns".equals(prefix)) { - String pre = getLocal(prefix, qname); - String uri = attrs.getValue(i); - if (!settings.contains(Setting.ManualNamespaces) && pre.contains("_")) - continue; // not permitted - context.setNamespaceURI(pre, uri); - extractor.setNamespaceURI(pre, uri); - sink.addPrefix(pre, uri); - } - } - } - - private String getPrefix(String qname) { - if (!qname.contains(":")) { - return ""; - } - return qname.substring(0, qname.indexOf(":")); - } - - private String getLocal(String prefix, String qname) { - if (prefix.length() == 0) { - return qname; - } - return qname.substring(prefix.length() + 1); - } - - private Iterator fromAttributes(Attributes attributes) { - List toReturn = new ArrayList<>(); - - for (int i = 0; i < attributes.getLength(); i++) { - String qname = attributes.getQName(i); - String prefix = qname.contains(":") ? qname.substring(0, qname.indexOf(":")) : ""; - Attribute attr = - eventFactory.createAttribute( - prefix, attributes.getURI(i), attributes.getLocalName(i), attributes.getValue(i)); - - if (!qname.equals("xmlns") && !qname.startsWith("xmlns:")) toReturn.add(attr); - } - - return toReturn.iterator(); - } - - private Attribute findAttribute(StartElement element, QName... names) { - for (QName aName : names) { - Attribute a = getAttributeByName(element, aName); - if (a != null) { - return a; - } - } - return null; - } - - private void parsePrefixes(String value, EvalContext context) { - String[] parts = value.split("\\s+"); - for (int i = 0; i < parts.length; i += 2) { - String prefix = parts[i]; - if (i + 1 < parts.length && prefix.endsWith(":")) { - String prefixFix = prefix.substring(0, prefix.length() - 1); - context.setPrefix(prefixFix, parts[i + 1]); - sink.addPrefix(prefixFix, parts[i + 1]); - } - } - } - - private Attribute getAttributeByName(StartElement element, QName name) { - if (name == null || element == null) { - return null; - } - Iterator it = element.getAttributes(); - while (it.hasNext()) { - Attribute at = it.next(); - if (Util.qNameEquals(at.getName(), name)) { - return at; - } - } - return null; - } - - int bnodeId = 0; - - private String createBNode() // TODO probably broken? Can you write bnodes - // in rdfa directly? - { - return "_:node" + (bnodeId++); - } - - private String getDatatype(StartElement element) { - Attribute de = getAttributeByName(element, Constants.datatype); - if (de == null) { - return null; - } - String dt = de.getValue(); - if (dt.length() == 0) { - return dt; - } - return extractor.expandCURIE(element, dt, context); - } -} diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/SAXRDFaParser.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/SAXRDFaParser.java deleted file mode 100644 index 6b76916886..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/SAXRDFaParser.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.Collection; -import javax.xml.stream.XMLEventFactory; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.events.XMLEvent; -import net.rootdev.javardfa.uri.IRIResolver; -import org.xml.sax.Attributes; -import org.xml.sax.Locator; -import org.xml.sax.SAXException; - -/** A RDFa parser for SAX */ -public class SAXRDFaParser extends RDFaParser { - - public static SAXRDFaParser createInstance(JenaSink sink) { - URIExtractor extractor = new URIExtractorImpl(new IRIResolver(), true); - sink.setExtractor(extractor); - return new SAXRDFaParser( - sink, XMLOutputFactory.newInstance(), XMLEventFactory.newInstance(), extractor); - } - - private SAXRDFaParser( - JenaSink sink, - XMLOutputFactory outputFactory, - XMLEventFactory eventFactory, - URIExtractor extractor) { - super(sink, outputFactory, eventFactory, extractor); - } - - public void emitTriples(String subj, Collection props, String obj) { - for (String prop : props) { - sink.addObject(subj, prop, obj); - } - } - - public void emitTriplesPlainLiteral( - String subj, Collection props, String lex, String language) { - for (String prop : props) { - sink.addLiteral(subj, prop, lex, language, null); - } - } - - public void emitTriplesDatatypeLiteral( - String subj, Collection props, String lex, String datatype) { - for (String prop : props) { - sink.addLiteral(subj, prop, lex, null, datatype); - } - } - - public void setDocumentLocator(Locator arg0) { - this.locator = arg0; - if (locator.getSystemId() != null) this.setBase(arg0.getSystemId()); - } - - public void startDocument() throws SAXException { - sink.start(); - } - - public void endDocument() throws SAXException { - sink.end(); - sink.setContext(context); - } - - public void startPrefixMapping(String arg0, String arg1) throws SAXException { - context.setNamespaceURI(arg0, arg1); - extractor.setNamespaceURI(arg0, arg1); - sink.addPrefix(arg0, arg1); - } - - public void endPrefixMapping(String arg0) throws SAXException {} - - public void startElement(String arg0, String localname, String qname, Attributes arg3) - throws SAXException { - super.beginRDFaElement(arg0, localname, qname, arg3); - } - - public void endElement(String arg0, String localname, String qname) throws SAXException { - super.endRDFaElement(arg0, localname, qname); - } - - public void characters(char[] arg0, int arg1, int arg2) throws SAXException { - super.writeCharacters(String.valueOf(arg0, arg1, arg2)); - } - - public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException { - // System.err.println("Whitespace..."); - if (literalCollector.isCollecting()) { - XMLEvent e = eventFactory.createIgnorableSpace(String.valueOf(arg0, arg1, arg2)); - literalCollector.handleEvent(e); - } - } - - public void processingInstruction(String arg0, String arg1) throws SAXException {} - - public void skippedEntity(String arg0) throws SAXException {} -} - -/* - * (c) Copyright 2009 University of Bristol All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. 2. Redistributions in - * binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. 3. The name of the author may not - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractor.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractor.java deleted file mode 100644 index e95ec8b4a1..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractor.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.List; -import java.util.Set; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; -import net.rootdev.javardfa.Setting; - -/** URIExtractor modified from net.rootdev.javardfa.uri.URIExtractor */ -public interface URIExtractor { - - void setSettings(Set settings); - - String expandCURIE(StartElement element, String value, EvalContext context); - - String expandSafeCURIE(StartElement element, String value, EvalContext context); - - String getURI(StartElement element, Attribute attr, EvalContext context); - - List getURIs(StartElement element, Attribute attr, EvalContext context); - - String resolveURI(String uri, EvalContext context); - - void setForSAX(boolean isForSAX); - - void setNamespaceURI(String prefix, String namespaceURI); -} - -/* - * (c) Copyright 2009 University of Bristol All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. 2. Redistributions in - * binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. 3. The name of the author may not - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractorImpl.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractorImpl.java deleted file mode 100644 index f4e79349c4..0000000000 --- a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/URIExtractorImpl.java +++ /dev/null @@ -1,202 +0,0 @@ -/** - * ********************************************************************** - * - *

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER - * - *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. - * - *

Use is subject to license terms. - * - *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0. You can also obtain a copy of the License at - * http://odftoolkit.org/docs/license.txt - * - *

Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. - * - *

See the License for the specific language governing permissions and limitations under the - * License. - * - *

********************************************************************** - */ -package org.odftoolkit.odfdom.pkg.rdfa; - -import java.util.Collections; -import java.util.HashMap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.xml.namespace.QName; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; -import net.rootdev.javardfa.Constants; -import net.rootdev.javardfa.Resolver; -import net.rootdev.javardfa.Setting; -import org.apache.commons.validator.routines.UrlValidator; - -/** URIExtractorImpl modified from net.rootdev.javardfa.uri.URIExtractor */ -class URIExtractorImpl implements URIExtractor { - private Set settings; - private final Resolver resolver; - private Map xmlnsMap = Collections.emptyMap(); - private boolean isForSAX; - private UrlValidator urlValidator; - - public URIExtractorImpl(Resolver resolver, boolean isForSAX) { - this.resolver = resolver; - this.isForSAX = isForSAX; - this.urlValidator = new UrlValidator(); - } - - public void setForSAX(boolean isForSAX) { - this.isForSAX = isForSAX; - } - - public void setSettings(Set settings) { - this.settings = settings; - } - - public String getURI(StartElement element, Attribute attr, EvalContext context) { - QName attrName = attr.getName(); - if (Util.qNameEquals(attrName, Constants.about)) // Safe CURIE or URI - { - return expandSafeCURIE(element, attr.getValue(), context); - } - if (Util.qNameEquals(attrName, Constants.datatype)) // A CURIE - { - return expandCURIE(element, attr.getValue(), context); - } - throw new RuntimeException("Unexpected attribute: " + attr); - } - - private boolean isValidURI(String uri) { - return this.urlValidator.isValid(uri); - } - - public List getURIs(StartElement element, Attribute attr, EvalContext context) { - - List uris = new ArrayList<>(); - - String[] curies = attr.getValue().split("\\s+"); - boolean permitReserved = - Util.qNameEquals(Constants.rel, attr.getName()) - || Util.qNameEquals(Constants.rev, attr.getName()); - for (String curie : curies) { - if (Constants.SpecialRels.contains(curie.toLowerCase())) { - if (permitReserved) uris.add("http://www.w3.org/1999/xhtml/vocab#" + curie.toLowerCase()); - } else { - String uri = expandCURIE(element, curie, context); - if (uri != null) { - uris.add(uri); - } - } - } - return uris; - } - - public String expandCURIE(StartElement element, String value, EvalContext context) { - - if (value.startsWith("_:")) { - if (!settings.contains(Setting.ManualNamespaces)) return value; - if (element.getNamespaceURI("_") == null) return value; - } - if (settings.contains(Setting.FormMode) - && // variable - value.startsWith("?")) { - return value; - } - int offset = value.indexOf(":") + 1; - if (offset == 0) { - return null; - } - String prefix = value.substring(0, offset - 1); - - // Apparently these are not allowed to expand - if ("xml".equals(prefix) || "xmlns".equals(prefix)) return null; - - String namespaceURI = null; - if (prefix.length() == 0) { - namespaceURI = "http://www.w3.org/1999/xhtml/vocab#"; - } else { - namespaceURI = element.getNamespaceURI(prefix); - if (isForSAX) { - if (namespaceURI != null) { - if (xmlnsMap == Collections.EMPTY_MAP) xmlnsMap = new HashMap<>(); - xmlnsMap.put(prefix, namespaceURI); - } - } else { - if (namespaceURI == null) { - namespaceURI = xmlnsMap.get(prefix); - } - } - } - if (namespaceURI == null) { - return null; - // throw new RuntimeException("Unknown prefix: " + prefix); - } - - return namespaceURI + value.substring(offset); - } - - @Override - public String expandSafeCURIE(StartElement element, String value, EvalContext context) { - if (value.startsWith("[") && value.endsWith("]")) { - return expandCURIE(element, value.substring(1, value.length() - 1), context); - } else { - if (value.length() == 0) { - return context.getBase(); - } - - if (settings.contains(Setting.FormMode) && value.startsWith("?")) { - return value; - } - - // earlier "return resolver.resolve(context.getBase(), value);" - // now has JENA problem with '/' slash as base URL - // Code: 57/REQUIRED_COMPONENT_MISSING in SCHEME: A component that is required by the - // scheme is missing. - return value; - } - } - - public String resolveURI(String uri, EvalContext context) { - return resolver.resolve(context.getBase(), uri); - } - - public String getNamespaceURI(String prefix) { - return xmlnsMap.getOrDefault(prefix, null); - } - - public void setNamespaceURI(String prefix, String namespaceURI) { - if (xmlnsMap == Collections.EMPTY_MAP) xmlnsMap = new HashMap<>(); - xmlnsMap.put(prefix, namespaceURI); - } -} - -/* - * (c) Copyright 2009 University of Bristol All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. 2. Redistributions in - * binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. 3. The name of the author may not - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/package-info.java b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/package-info.java new file mode 100644 index 0000000000..131529322a --- /dev/null +++ b/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/rdfa/package-info.java @@ -0,0 +1,88 @@ +/* + * Copyright 2026 The Document Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Support for ODF in-content metadata, i.e. RDF statements attached to elements of the + * content.xml and styles.xml files. + * + *

In-content metadata in ODF

+ * + *

Besides RDF/XML files registered in the package's manifest.rdf (ODF 1.2 Part 3, + * "Metadata Manifest"), ODF allows RDF metadata within the document content (ODF 1.2 Part 1, + * §4.2.1 "In Content Metadata (RDFa)"). It borrows four attributes and their data types from + * RDFa 1.0: xhtml:about, xhtml:property, xhtml:datatype + * and xhtml:content. Unlike RDFa in XHTML, the ODF schema only allows them on a few + * elements and always demands xhtml:about and xhtml:property together. + * Other RDFa attributes like rel, rev, typeof or + * resource and the RDFa inheritance of subjects between nested elements do not exist in + * ODF. For example, the paragraph + * + *

+ * <text:p xhtml:about="[dbpedia:J._K._Rowling]" xhtml:property="dbpprop:birthDate"
+ *     xhtml:datatype="xsd:date" xhtml:content="1965-07-31">July 31st, 1965</text:p>
+ * 
+ * + * states the RDF statement <http://dbpedia.org/page/J._K._Rowling> + * <http://dbpedia.org/property/birthDate> "1965-07-31"^^xsd:date, given the + * corresponding namespace declarations. The detailed rules are documented at {@link + * org.odftoolkit.odfdom.pkg.rdfa.InContentMetadata}. + * + *

Two ways of retrieving in-content metadata

+ * + *
    + *
  1. From the XML files by GRDDL: {@link + * org.odftoolkit.odfdom.dom.OdfSchemaDocument#getInContentMetadata()} and {@link + * org.odftoolkit.odfdom.dom.OdfSchemaDocument#getRDFMetadata()} transform the XML files + * with the XSLT stylesheet grddl/odf2rdf.xsl into RDF/XML, which Jena parses. + * This way does not use this package, except for {@link + * org.odftoolkit.odfdom.pkg.rdfa.Util}, and reflects the content as stored in the package. + *
  2. From the DOM: {@link + * org.odftoolkit.odfdom.dom.OdfSchemaDocument#getInContentMetadataFromCache()} merges the + * per element cache of each XML file, see {@link + * org.odftoolkit.odfdom.pkg.OdfFileDom#getInContentMetadataCache()}. This way reflects the + * current state of the DOM, including changes not yet saved. The metadata of bookmarks is + * available by {@link org.odftoolkit.odfdom.pkg.OdfFileDom#getBookmarkRDFMetadata()} and + * {@link org.odftoolkit.odfdom.dom.rdfa.BookmarkRDFMetadataExtractor}. + *
+ * + *

Life cycle of the DOM cache

+ * + *

The cache is created, when it is first requested, by a single traversal of the DOM with + * {@link org.odftoolkit.odfdom.pkg.rdfa.InContentMetadata#collect(org.w3c.dom.Node, + * java.util.Map)}. Loading a document therefore costs nothing for users not interested in RDF. + * Afterwards, the generated classes of the six elements allowing in-content metadata keep the + * cache up to date: they call {@link + * org.odftoolkit.odfdom.pkg.OdfFileDom#updateInContentMetadataCache(org.w3c.dom.Node)} when + * their text content is replaced or when they are inserted into the DOM, and remove their entry + * when they are removed from the DOM. Other changes, like setting an xhtml:* + * attribute or changing the text of a descendant element, require an explicit call of {@link + * org.odftoolkit.odfdom.pkg.OdfFileDom#updateInContentMetadataCache(org.w3c.dom.Node)}. + * + *

History

+ * + *

Up to ODFDOM 0.13.0, the DOM cache was filled by a modified copy of the general purpose RDFa + * 1.0 parser net.rootdev:java-rdfa:1.0.0-BETA1, which ran as a second SAX handler + * while loading every XML file. That library is no longer maintained and depends on the + * jena-iri library, which Apache Jena 6 no longer provides. As ODF only uses the small, + * self-contained subset of RDFa described above, it is now implemented by {@link + * org.odftoolkit.odfdom.pkg.rdfa.InContentMetadata} directly. The following public classes of + * this package, which were only used internally for the RDFa parsing, have been removed: + * DOMAttributes, DOMRDFaParser, JenaSink, + * MultiContentHandler, SAXRDFaParser and URIExtractor, together + * with OdfFileDom.getSink() and OdfFileSaxHandler.setSink(JenaSink). + * The API returning Jena models is unchanged. + */ +package org.odftoolkit.odfdom.pkg.rdfa; diff --git a/odfdom/src/test/java/org/odftoolkit/odfdom/pkg/RDFMetadataTest.java b/odfdom/src/test/java/org/odftoolkit/odfdom/pkg/RDFMetadataTest.java index 5cda820df2..8844527ae7 100644 --- a/odfdom/src/test/java/org/odftoolkit/odfdom/pkg/RDFMetadataTest.java +++ b/odfdom/src/test/java/org/odftoolkit/odfdom/pkg/RDFMetadataTest.java @@ -23,15 +23,23 @@ */ package org.odftoolkit.odfdom.pkg; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.util.logging.Logger; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import junit.framework.TestCase; +import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; import org.junit.Test; import org.odftoolkit.odfdom.doc.OdfDocument; import org.odftoolkit.odfdom.doc.OdfTextDocument; import org.odftoolkit.odfdom.dom.OdfContentDom; +import org.odftoolkit.odfdom.dom.element.office.OfficeTextElement; +import org.odftoolkit.odfdom.dom.element.text.TextHElement; +import org.odftoolkit.odfdom.dom.element.text.TextPElement; import org.odftoolkit.odfdom.dom.element.text.TextBookmarkStartElement; import org.odftoolkit.odfdom.dom.rdfa.BookmarkRDFMetadataExtractor; import org.odftoolkit.odfdom.utils.ResourceUtilities; @@ -41,6 +49,43 @@ public class RDFMetadataTest { private static final Logger LOG = Logger.getLogger(RDFMetadataTest.class.getName()); private static final String SIMPLE_ODT = "test_rdfmeta.odt"; + // Refs #445: Jena 6 needs ARQ to preserve RDF/XML metadata support. + @Test + public void testRdfXmlRoundTrip() { + Model original = ModelFactory.createDefaultModel(); + Model restored = ModelFactory.createDefaultModel(); + try { + Resource document = original.createResource("https://example.org/document.odt"); + Resource creator = original.createResource(); + original.add( + document, + original.createProperty("http://purl.org/dc/elements/1.1/title"), + original.createLiteral("Grüße aus Berlin – 東京", "de")); + original.add( + document, original.createProperty("http://purl.org/dc/elements/1.1/creator"), creator); + original.add( + creator, + original.createProperty("http://xmlns.com/foaf/0.1/name"), + original.createLiteral("Zoë")); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + original.write(output, "RDF/XML"); + restored.read(new ByteArrayInputStream(output.toByteArray()), null, "RDF/XML"); + + TestCase.assertTrue( + "RDF/XML round-trip must preserve the RDF graph", original.isIsomorphicWith(restored)); + TestCase.assertEquals( + "de", + restored.getRequiredProperty( + restored.getResource(document.getURI()), + restored.createProperty("http://purl.org/dc/elements/1.1/title")) + .getLanguage()); + } finally { + restored.close(); + original.close(); + } + } + @Test public void testGetRDFMetaFromGRDDLXSLT() throws Exception { OdfTextDocument odt = @@ -273,4 +318,64 @@ public void testGetBookmarkRDFMetadata() throws Exception { m = extractor.getBookmarkRDFMetadata(tm); TestCase.assertEquals(1, m.size()); } + + /** + * The DOM cache of in-content metadata is built on demand and follows insertions, text changes + * and removals of elements carrying RDFa attributes. + */ + @Test + public void testGetInContentMetadataFromCache() throws Exception { + OdfTextDocument odt = + (OdfTextDocument) + OdfDocument.loadDocument(ResourceUtilities.getAbsoluteInputPath(SIMPLE_ODT)); + Model m = odt.getInContentMetadataFromCache(); + // the of the document; its nested bookmarks are covered by getBookmarkRDFMetadata() + TestCase.assertEquals(1, m.size()); + TestCase.assertEquals( + "John Ronald Reuel Tolkien", + m.getRequiredProperty( + m.getResource("http://dbpedia.org/page/J._R._R._Tolkien"), + m.getProperty("http://www.w3.org/2006/vcard/ns#fn")) + .getString()); + + // safe CURIE subject, two predicates, a typed literal and xhtml:content overriding the text + OdfContentDom contentDom = odt.getContentDom(); + OfficeTextElement text = odt.getContentRoot(); + TextPElement p = contentDom.newOdfElement(TextPElement.class); + p.setXhtmlAboutAttribute("[dbpedia:J._K._Rowling]"); + p.setXhtmlPropertyAttribute("dbpprop:birthDate dbpprop:dateOfBirth"); + p.setXhtmlDatatypeAttribute("xsd:date"); + p.setXhtmlContentAttribute("1965-07-31"); + p.setTextContent("July 31st, 1965"); + text.appendChild(p); + Model pModel = contentDom.getInContentMetadataCache().get(p); + TestCase.assertEquals(2, pModel.size()); + Resource rowling = pModel.getResource("http://dbpedia.org/page/J._K._Rowling"); + for (String property : new String[] {"birthDate", "dateOfBirth"}) { + String predicate = "http://dbpedia.org/property/" + property; + Literal date = + pModel.getRequiredProperty(rowling, pModel.getProperty(predicate)).getLiteral(); + TestCase.assertEquals("1965-07-31", date.getLexicalForm()); + TestCase.assertEquals("http://www.w3.org/2001/XMLSchema#date", date.getDatatypeURI()); + } + TestCase.assertEquals(3, odt.getInContentMetadataFromCache().size()); + + // the element text is the literal, replacing the text updates the cache + TextHElement h = contentDom.newOdfElement(TextHElement.class); + h.setXhtmlAboutAttribute("http://dbpedia.org/page/J._K._Rowling"); + h.setXhtmlPropertyAttribute("dbpprop:children"); + h.setXhtmlDatatypeAttribute("xsd:integer"); + h.setTextContent("2"); + text.appendChild(h); + h.setTextContent(" 3 "); + Model hModel = contentDom.getInContentMetadataCache().get(h); + TestCase.assertEquals(1, hModel.size()); + TestCase.assertEquals( + "3", hModel.listStatements().nextStatement().getLiteral().getLexicalForm()); + + // removed elements take their statements with them + text.removeChild(p); + text.removeChild(h); + TestCase.assertEquals(1, odt.getInContentMetadataFromCache().size()); + } } diff --git a/pom.xml b/pom.xml index 6c79ba9769..1eeeb2dffc 100644 --- a/pom.xml +++ b/pom.xml @@ -34,10 +34,11 @@ Copyright © {inceptionYear}–2018 Apache Software Foundation; Copyright © 2018–${currentYear} {organizationName}. All rights reserved. UTF-8 - 17 + 21 - 17 - 17 + 21 + 21 + 6.2.0 ${basedir}/target/release ${release.dir}/${project.version}/binaries @@ -73,11 +74,6 @@ Making version management easy and consistent! --> - - commons-validator - commons-validator - 1.10.1 - commons-fileupload commons-fileupload @@ -93,11 +89,6 @@ msv-core 2022.7 - - net.rootdev - java-rdfa - 1.0.0-BETA1 - org.apache.ant ant @@ -107,7 +98,13 @@ org.apache.jena jena-core - 5.6.0 + ${jena.version} + + + + org.apache.jena + jena-arq + ${jena.version} From 0caaa22eeda160d6131393292f2e19ac1300a063 Mon Sep 17 00:00:00 2001 From: Svante Schubert Date: Tue, 29 Sep 2026 12:40:06 +0200 Subject: [PATCH 2/2] Update Eclipse project metadata Align odfdom JDT compiler settings with Java 21 and refresh the resource filters written by the Java language server. --- .project | 4 ++-- generator/.project | 4 ++-- generator/schema2template-maven-plugin/.project | 4 ++-- generator/schema2template/.project | 4 ++-- odfdom/.project | 4 ++-- odfdom/.settings/org.eclipse.jdt.core.prefs | 8 ++++---- taglets/.project | 4 ++-- validator/.project | 4 ++-- xslt-runner/.project | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.project b/.project index 6afa65d285..dce7d54d4b 100644 --- a/.project +++ b/.project @@ -16,12 +16,12 @@ - 1659282302444 + 1790677806650 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/generator/.project b/generator/.project index f10d2b92b9..5b06d67164 100644 --- a/generator/.project +++ b/generator/.project @@ -16,12 +16,12 @@ - 1659282302476 + 1790677806658 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/generator/schema2template-maven-plugin/.project b/generator/schema2template-maven-plugin/.project index 88e7e045b2..a06fd13787 100644 --- a/generator/schema2template-maven-plugin/.project +++ b/generator/schema2template-maven-plugin/.project @@ -22,12 +22,12 @@ - 1659282302468 + 1790677806657 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/generator/schema2template/.project b/generator/schema2template/.project index 8aac1bc1d3..c1ea1fde72 100644 --- a/generator/schema2template/.project +++ b/generator/schema2template/.project @@ -22,12 +22,12 @@ - 1659282302459 + 1790677806655 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/odfdom/.project b/odfdom/.project index 48563ca555..c46599c205 100644 --- a/odfdom/.project +++ b/odfdom/.project @@ -22,12 +22,12 @@ - 1659282302431 + 1790677806648 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/odfdom/.settings/org.eclipse.jdt.core.prefs b/odfdom/.settings/org.eclipse.jdt.core.prefs index 46235dc078..e96c0486a1 100644 --- a/odfdom/.settings/org.eclipse.jdt.core.prefs +++ b/odfdom/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore org.eclipse.jdt.core.compiler.processAnnotations=disabled -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/taglets/.project b/taglets/.project index eec8a43cae..106eae5742 100644 --- a/taglets/.project +++ b/taglets/.project @@ -22,12 +22,12 @@ - 1659282302483 + 1790677806659 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/validator/.project b/validator/.project index 325a5959bf..5d16429e17 100644 --- a/validator/.project +++ b/validator/.project @@ -29,12 +29,12 @@ - 1659282302451 + 1790677806652 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/xslt-runner/.project b/xslt-runner/.project index e3521ac9c7..28ec205880 100644 --- a/xslt-runner/.project +++ b/xslt-runner/.project @@ -22,12 +22,12 @@ - 1659282302490 + 1790677806660 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__