Skip to content

Commit

Permalink
Merge pull request objectionary#3929 from maxonfjvipon/bug/objectiona…
Browse files Browse the repository at this point in the history
…ry#3927/xpath-to-xnav-unhexing

bug(objectionary#3927): rewrite `StUnhex` with `Xnav`
  • Loading branch information
yegor256 authored Feb 18, 2025
2 parents 031ed82 + 176fe81 commit f121fe7
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 122 deletions.
141 changes: 92 additions & 49 deletions eo-parser/src/main/java/org/eolang/parser/StUnhex.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,65 +24,117 @@
package org.eolang.parser;

import com.yegor256.xsline.Shift;
import com.yegor256.xsline.StEndless;
import com.yegor256.xsline.StClasspath;
import com.yegor256.xsline.StEnvelope;
import com.yegor256.xsline.StSequence;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.apache.commons.text.StringEscapeUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xembly.Directive;
import org.xembly.Directives;

/**
* This {@link Shift} turns hex data inside XMIR
* This {@link Shift} turns hex data inside XMIR.
* into EO-printable data.
*
* @since 0.29.0
*/
final class StUnhex extends StEnvelope {
/**
* Unexing via {@link com.github.lombrozo.xnav.Xnav}.
*/
static final Shift XNAV = new StSequence(
StUnhex.class.getSimpleName(),
new StXnav(
StUnhex.elements("number"),
xnav -> {
final double number = StUnhex.buffer(
StUnhex.undash(xnav.element("o").text().orElse(""))
).getDouble();
final Node node = xnav.node();
if (Double.isNaN(number) || Double.isInfinite(number)) {
((Element) node).setAttribute("skip", "");
} else {
node.setTextContent(StUnhex.number(number));
}
}
),
new StXnav(
StUnhex.elements("string"),
xnav -> xnav.node().setTextContent(
String.format(
"\"%s\"",
StringEscapeUtils.escapeJava(
new String(
StUnhex.buffer(
StUnhex.undash(xnav.element("o").text().orElse(""))
).array(),
StandardCharsets.UTF_8
)
)
)
)
)
);

/**
* Ctor.
* Unhexing via {@link com.jcabi.xml.XMLDocument#xpath(String)}.
*/
StUnhex() {
super(
new StEndless(
new StSequence(
StUnhex.class.getSimpleName(),
new StXPath(
StUnhex.xpath("number"),
xml -> {
final double number = StUnhex.buffer(
static final Shift XPATH = new StSequence(
StUnhex.class.getSimpleName(),
new StXPath(
StUnhex.elements("number"),
xml -> {
final double number = StUnhex.buffer(
StUnhex.undash(xml.xpath("./o/text()").get(0))
).getDouble();
final Iterable<Directive> dirs;
if (Double.isNaN(number) || Double.isInfinite(number)) {
dirs = new Directives().attr("skip", "");
} else {
dirs = new Directives().set(StUnhex.number(number));
}
return dirs;
}
),
new StXPath(
StUnhex.elements("string"),
xml -> new Directives().set(
String.format(
"\"%s\"",
StringEscapeUtils.escapeJava(
new String(
StUnhex.buffer(
StUnhex.undash(xml.xpath("./o/text()").get(0))
).getDouble();
final Iterable<Directive> dirs;
if (Double.isNaN(number) || Double.isInfinite(number)) {
dirs = new Directives().attr("skip", "");
} else {
dirs = StUnhex.append(StUnhex.number(number));
}
return dirs;
}
),
new StXPath(
StUnhex.xpath("string"),
xml -> StUnhex.append(
String.format(
"\"%s\"",
StringEscapeUtils.escapeJava(
new String(
StUnhex.buffer(
StUnhex.undash(xml.xpath("./o/text()").get(0))
).array(),
StandardCharsets.UTF_8
)
)
)
).array(),
StandardCharsets.UTF_8
)
)
)
)
);
)
);

/**
* Unhexing via XSL.
*/
static final Shift XSL = new StClasspath("/org/eolang/parser/print/unhex-data.xsl");

/**
* Ctor.
*/
StUnhex() {
this(StUnhex.XNAV);
}

/**
* Base ctor.
* @param origin Original shift
*/
StUnhex(final Shift origin) {
super(origin);
}

/**
Expand Down Expand Up @@ -142,23 +194,14 @@ private static String undash(final String txt) {
}

/**
* Make XPath.
* Find elements by XPath for given type.
* @param type The type to match
* @return XPath
*/
private static String xpath(final String type) {
private static String elements(final String type) {
return String.format(
"(//o[@base='Q.org.eolang.%1$s' and(not(@skip)) and o[1][@base='Q.org.eolang.bytes' and not(o) and string-length(normalize-space(text()))>0]])[1]",
"//o[@base='Q.org.eolang.%1$s' and(not(@skip)) and o[1][@base='Q.org.eolang.bytes' and not(o) and string-length(normalize-space(text()))>0]]",
type
);
}

/**
* Append Xemply instructions.
* @param after Value after
* @return Dirs
*/
private static Iterable<Directive> append(final String after) {
return new Directives().set(after);
}
}
31 changes: 17 additions & 14 deletions eo-parser/src/main/java/org/eolang/parser/StXPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
* @since 0.29.0
*/
public final class StXPath implements Shift {
/**
* Xpath for finding first element.
*/
private static final String INDEXED = "(%s)[1]";

/**
* XPath to search for.
Expand Down Expand Up @@ -68,22 +72,21 @@ public String uid() {
@Override
public XML apply(final int position, final XML xml) {
final List<XML> nodes = xml.nodes(this.xpath);
if (nodes.size() > 1) {
throw new IllegalArgumentException(
String.format(
"XPath '%s' returned too many elements (%d)",
this.xpath, nodes.size()
)
final XML doc;
if (nodes.isEmpty()) {
doc = xml;
} else {
final Directives dirs = new Directives();
for (final XML node : nodes) {
dirs.xpath(String.format(StXPath.INDEXED, this.xpath))
.strict(1)
.append(this.fun.apply(node));
}
doc = new XMLDocument(
new Xembler(dirs).applyQuietly(xml.inner())
);
}
final Directives dirs = new Directives();
if (!nodes.isEmpty()) {
dirs.xpath(this.xpath);
dirs.append(this.fun.apply(nodes.get(0)));
}
return new XMLDocument(
new Xembler(dirs).applyQuietly(xml.deepCopy())
);
return doc;
}

}
71 changes: 71 additions & 0 deletions eo-parser/src/main/java/org/eolang/parser/StXnav.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016-2025 Objectionary.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.eolang.parser;

import com.github.lombrozo.xnav.Xnav;
import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import com.yegor256.xsline.Shift;
import java.util.function.Consumer;
import org.w3c.dom.Node;

/**
* This {@link Shift} finds all XPath matches and replaces them
* with what a function suggests using {@link com.github.lombrozo.xnav.Xnav}.
*
* @since 0.53.0
*/
public final class StXnav implements Shift {
/**
* XPath to search for.
*/
private final String xpath;

/**
* The mapping function.
*/
private final Consumer<Xnav> fun;

/**
* Ctor.
* @param path The XPath
* @param func The function
*/
public StXnav(final String path, final Consumer<Xnav> func) {
this.xpath = path;
this.fun = func;
}

@Override
public String uid() {
return this.getClass().getSimpleName();
}

@Override
public XML apply(final int position, final XML xml) {
final Node dom = xml.inner();
new Xnav(dom).path(this.xpath).forEach(this.fun);
return new XMLDocument(dom);
}
}
Loading

0 comments on commit f121fe7

Please sign in to comment.