From 6766b15d865ada08a1dfd31d60a73b418dee059c Mon Sep 17 00:00:00 2001 From: Yegor Bugayenko Date: Sat, 7 Dec 2019 12:46:53 +0300 Subject: [PATCH] #95 comment() --- README.md | 1 + src/main/antlr3/org/xembly/Xembly.g | 9 ++ .../java/org/xembly/CommentDirective.java | 84 +++++++++++++++++++ src/main/java/org/xembly/Directives.java | 28 ++++++- .../java/org/xembly/CommentDirectiveTest.java | 63 ++++++++++++++ src/test/java/org/xembly/DirectivesTest.java | 16 ++++ src/test/java/org/xembly/XemblerTest.java | 40 --------- 7 files changed, 200 insertions(+), 41 deletions(-) create mode 100644 src/main/java/org/xembly/CommentDirective.java create mode 100644 src/test/java/org/xembly/CommentDirectiveTest.java diff --git a/README.md b/README.md index bbf1ce3a..011f6a8d 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Full list of supported directives in the current version: * `PUSH`: saves cursor in stack * `POP`: retrieves cursor from stack * `NS`: sets namespace of all current nodes + * `COMMENT`: adds XML comment "Cursor" or "current nodes" is where we're currently located in the XML document. When Xembly script starts, the cursor is diff --git a/src/main/antlr3/org/xembly/Xembly.g b/src/main/antlr3/org/xembly/Xembly.g index e4937c5b..929ed9ac 100644 --- a/src/main/antlr3/org/xembly/Xembly.g +++ b/src/main/antlr3/org/xembly/Xembly.g @@ -163,6 +163,15 @@ directive returns [Directive ret] throw new ParsingException(ex); } } + | + 'COMMENT' argument + { + try { + $ret = new CommentDirective($argument.ret.toString()); + } catch (final XmlContentException ex) { + throw new ParsingException(ex); + } + } ; argument returns [Object ret] diff --git a/src/main/java/org/xembly/CommentDirective.java b/src/main/java/org/xembly/CommentDirective.java new file mode 100644 index 00000000..2ed5ceee --- /dev/null +++ b/src/main/java/org/xembly/CommentDirective.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2013-2019, xembly.org + * 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) Neither the name of the xembly.org nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ +package org.xembly; + +import lombok.EqualsAndHashCode; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * COMMENT directive. + * + *

The class is immutable and thread-safe. + * + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + * @since 0.23 + */ +@EqualsAndHashCode(of = "value") +final class CommentDirective implements Directive { + + /** + * Text value to set. + */ + private final transient Arg value; + + /** + * Public ctor. + * @param val Text value to set + * @throws XmlContentException If invalid input + */ + CommentDirective(final String val) throws XmlContentException { + this.value = new Arg(val); + } + + @Override + public String toString() { + return String.format("CDATA %s", this.value); + } + + @Override + public Directive.Cursor exec(final Node dom, + final Directive.Cursor cursor, final Directive.Stack stack) { + final Document doc; + if (dom.getOwnerDocument() == null) { + doc = Document.class.cast(dom); + } else { + doc = dom.getOwnerDocument(); + } + final String val = this.value.raw(); + for (final Node node : cursor) { + final Node cdata = doc.createComment(val); + node.appendChild(cdata); + } + return cursor; + } + +} diff --git a/src/main/java/org/xembly/Directives.java b/src/main/java/org/xembly/Directives.java index 6478285a..a6e8980f 100644 --- a/src/main/java/org/xembly/Directives.java +++ b/src/main/java/org/xembly/Directives.java @@ -528,7 +528,33 @@ public Directives cdata(final Object text) { } catch (final XmlContentException ex) { throw new IllegalArgumentException( String.format( - "failed to understand XML content, CDATA(%s)", + "Failed to understand XML content, CDATA(%s)", + text + ), + ex + ); + } + return this; + } + + /** + * Add an XML comment. + * + *

If a value provided contains illegal XML characters, a runtime + * exception will be thrown. To avoid this, it is recommended to use + * {@link Xembler#escape(String)}. + * + * @param text Text to set + * @return This object + * @since 0.23 + */ + public Directives comment(final Object text) { + try { + this.all.add(new CommentDirective(text.toString())); + } catch (final XmlContentException ex) { + throw new IllegalArgumentException( + String.format( + "Failed to understand XML content, comment(%s)", text ), ex diff --git a/src/test/java/org/xembly/CommentDirectiveTest.java b/src/test/java/org/xembly/CommentDirectiveTest.java new file mode 100644 index 00000000..02fdfdab --- /dev/null +++ b/src/test/java/org/xembly/CommentDirectiveTest.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2013-2019, xembly.org + * 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) Neither the name of the xembly.org nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. + */ +package org.xembly; + +import com.jcabi.matchers.XhtmlMatchers; +import javax.xml.parsers.DocumentBuilderFactory; +import org.hamcrest.MatcherAssert; +import org.junit.Test; +import org.w3c.dom.Document; + +/** + * Test case for {@link CommentDirective}. + * @author Yegor Bugayenko (yegor256@gmail.com) + * @version $Id$ + */ +public final class CommentDirectiveTest { + + /** + * CommentDirective can add a comment. + * @throws Exception If some problem inside + */ + @Test + public void addsComment() throws Exception { + final Iterable dirs = new Directives( + "ADD 'root'; ADD 'foo'; COMMENT 'How are you?';" + ); + final Document dom = DocumentBuilderFactory.newInstance() + .newDocumentBuilder().newDocument(); + new Xembler(dirs).apply(dom); + MatcherAssert.assertThat( + XhtmlMatchers.xhtml(dom), + XhtmlMatchers.hasXPath("//comment()") + ); + } + +} diff --git a/src/test/java/org/xembly/DirectivesTest.java b/src/test/java/org/xembly/DirectivesTest.java index 705d9293..02a67e57 100644 --- a/src/test/java/org/xembly/DirectivesTest.java +++ b/src/test/java/org/xembly/DirectivesTest.java @@ -319,4 +319,20 @@ public Void call() throws Exception { ); } + /** + * Directives can add comments. + * @throws Exception If some problem inside + */ + @Test + public void addsComments() throws Exception { + MatcherAssert.assertThat( + new Xembler( + new Directives() + .add("victory") + .comment(Xembler.escape("Yes, we !")) + ).xml(), + XhtmlMatchers.hasXPath("//comment()") + ); + } + } diff --git a/src/test/java/org/xembly/XemblerTest.java b/src/test/java/org/xembly/XemblerTest.java index 45aa82fc..31e81097 100644 --- a/src/test/java/org/xembly/XemblerTest.java +++ b/src/test/java/org/xembly/XemblerTest.java @@ -55,46 +55,6 @@ @SuppressWarnings("PMD.TooManyMethods") public final class XemblerTest { - - @Test - public void test() throws Exception { - - - // 'Object Thinking' - - // create "books" - // add "book" - // change the content of "book" to "Object Thinking" - - final Node node = new XMLDocument( - "War and Peace" - ).node(); - Directives dirs = new Directives() - .xpath("/books/book") - .strict(1) - .remove() - .add("book") - .set("Elegant Objects:\n\r$40.96") - .attr("id", 123); - new Xembler(dirs).apply(node); - System.out.println(new XMLDocument(node).toString()); - } - - - - - - - - - - - - - - - - /** * Xembler can change DOM document. * @throws Exception If some problem inside