Skip to content

Commit

Permalink
Merge branch '__rultor'
Browse files Browse the repository at this point in the history
  • Loading branch information
rultor committed Dec 2, 2024
2 parents de0e31a + 157b83b commit 717f728
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 70 deletions.
11 changes: 8 additions & 3 deletions src/main/java/org/eolang/jeo/Assembling.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public final class Assembling implements Transformation {
*/
private final Path from;

/**
* XMIR representation.
*/
private final XmirRepresentation repr;

/**
* Constructor.
* @param target Target folder.
Expand All @@ -52,6 +57,7 @@ public final class Assembling implements Transformation {
Assembling(final Path target, final Path representation) {
this.folder = target;
this.from = representation;
this.repr = new XmirRepresentation(this.from);
}

@Override
Expand All @@ -61,15 +67,14 @@ public Path source() {

@Override
public Path target() {
final XmirRepresentation repr = new XmirRepresentation(this.from);
final String name = new PrefixedName(repr.name()).decode();
final String name = new PrefixedName(this.repr.name()).decode();
final String[] subpath = name.split("\\.");
subpath[subpath.length - 1] = String.format("%s.class", subpath[subpath.length - 1]);
return Paths.get(this.folder.toString(), subpath);
}

@Override
public byte[] transform() {
return new XmirRepresentation(this.from).toBytecode().bytes();
return this.repr.toBytecode().bytes();
}
}
145 changes: 125 additions & 20 deletions src/main/java/org/eolang/jeo/representation/XmirRepresentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,27 @@
package org.eolang.jeo.representation;

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.cactoos.io.ResourceOf;
import org.cactoos.io.UncheckedInput;
import org.cactoos.scalar.Sticky;
import org.cactoos.scalar.Synced;
import org.cactoos.scalar.Unchecked;
import org.eolang.jeo.representation.bytecode.Bytecode;
import org.eolang.jeo.representation.xmir.XmlProgram;
import org.eolang.parser.Schema;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

/**
* Intermediate representation of a class files from XMIR.
Expand All @@ -41,10 +53,20 @@
*/
public final class XmirRepresentation {

/**
* XPath's factory.
*/
private static final XPathFactory XPATH_FACTORY = XPathFactory.newInstance();

/**
* XML document factory.
*/
private static final DocumentBuilderFactory DOC_FACTORY = DocumentBuilderFactory.newInstance();

/**
* XML.
*/
private final Unchecked<XML> xml;
private final Unchecked<Node> xml;

/**
* Source of the XML.
Expand All @@ -64,7 +86,7 @@ public XmirRepresentation(final Path path) {
* @param xml XML.
*/
public XmirRepresentation(final XML xml) {
this(xml, "Unknown");
this(xml.node().getFirstChild(), "Unknown");
}

/**
Expand All @@ -73,7 +95,7 @@ public XmirRepresentation(final XML xml) {
* @param source Source of the XML.
*/
private XmirRepresentation(
final XML xml,
final Node xml,
final String source
) {
this(new Unchecked<>(() -> xml), source);
Expand All @@ -85,7 +107,7 @@ private XmirRepresentation(
* @param source Source of the XML.
*/
private XmirRepresentation(
final Unchecked<XML> xml,
final Unchecked<Node> xml,
final String source
) {
this.xml = xml;
Expand All @@ -94,27 +116,46 @@ private XmirRepresentation(

/**
* Retrieves class name from XMIR.
* This method intentionally uses classes from `org.w3c.dom` instead of `com.jcabi.xml`
* by performance reasons.
* @return Class name.
*/
public String name() {
return new ClassName(
this.xml.value()
.xpath("/program/metas/meta/tail/text()")
.stream()
.findFirst()
.orElse(""),
this.xml.value().xpath("/program/@name").get(0)
).full();
final Node node = this.xml.value();
final XPath xpath = XmirRepresentation.XPATH_FACTORY.newXPath();
try {
return new ClassName(
Optional.ofNullable(
((Node) xpath.evaluate(
"/program/metas/meta/tail/text()",
node,
XPathConstants.NODE
)).getTextContent()
).orElse(""),
String.valueOf(
xpath.evaluate(
"/program/@name",
node,
XPathConstants.STRING
)
)
).full();
} catch (final XPathExpressionException exception) {
throw new IllegalStateException(
String.format("Can't extract class name from the '%s' source", this.source),
exception
);
}
}

/**
* Convert to bytecode.
* @return Array of bytes.
*/
public Bytecode toBytecode() {
final XML xmir = this.xml.value();
final Node xmir = this.xml.value();
try {
new Schema(xmir).check();
new OptimizedSchema(xmir).check();
return new XmlProgram(xmir).bytecode().bytecode();
} catch (final IllegalArgumentException exception) {
throw new IllegalArgumentException(
Expand All @@ -134,24 +175,29 @@ public Bytecode toBytecode() {
* @param path Path to an XML file.
* @return Lazy XML.
*/
private static Unchecked<XML> fromFile(final Path path) {
private static Unchecked<Node> fromFile(final Path path) {
return new Unchecked<>(new Synced<>(new Sticky<>(() -> XmirRepresentation.open(path))));
}

/**
* Convert a path to XML.
* @param path Path to XML file.
* @return XML.
* @checkstyle IllegalCatchCheck (20 lines)
*/
private static XML open(final Path path) {
@SuppressWarnings("PMD.AvoidCatchingGenericException")
private static Node open(final Path path) {
try {
return new XMLDocument(path.toFile());
return XmirRepresentation.DOC_FACTORY
.newDocumentBuilder()
.parse(path.toFile())
.getDocumentElement();
} catch (final FileNotFoundException exception) {
throw new IllegalStateException(
String.format("Can't find file '%s'", path),
exception
);
} catch (final IllegalArgumentException broken) {
} catch (final Exception broken) {
throw new IllegalStateException(
String.format(
"Can't parse XML from the file '%s'",
Expand All @@ -161,4 +207,63 @@ private static XML open(final Path path) {
);
}
}

/**
* Optimized schema for XMIR.
* It is an optimized version of {@link org.eolang.parser.Schema} class.
* @since 0.6
* @todo #889:30min Use the `Schema` class instead of `OptimizedSchema`.
* The `OptimizedSchema` class is a temporary solution to avoid the performance
* issues with the `Schema` class. We will be able to remove this class after
* the following issue is resolved:
* https://github.com/jcabi/jcabi-xml/issues/277
*/
private static class OptimizedSchema {
/**
* Node.
*/
private final Node node;

/**
* Schema factory.
*/
private final SchemaFactory factory;

/**
* Constructor.
* @param node Node.
*/
OptimizedSchema(final Node node) {
this(node, SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"));
}

/**
* Constructor.
* @param node Node.
* @param factory Schema factory.
*/
private OptimizedSchema(final Node node, final SchemaFactory factory) {
this.node = node;
this.factory = factory;
}

/**
* Check the node.
*/
void check() {
try {
this.factory.newSchema(
new StreamSource(new UncheckedInput(new ResourceOf("XMIR.xsd")).stream())
).newValidator().validate(new DOMSource(this.node));
} catch (final IOException | SAXException exception) {
throw new IllegalStateException(
String.format(
"There are XSD violations, see the log",
exception.getMessage()
),
exception
);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,16 @@ private DirectivesSeq(final String name, final Iterable<Directive>... elements)

@Override
public Iterator<Directive> iterator() {
final List<Directives> all = this.stream()
.map(Directives::new)
.collect(Collectors.toList());
return new DirectivesJeoObject(
String.format("seq.of%d", this.size()),
String.format("seq.of%d", all.size()),
this.name,
this.stream().map(Directives::new).collect(Collectors.toList())
all
).iterator();
}

/**
* Size of the sequence.
* @return Size.
*/
private long size() {
return this.stream().count();
}

/**
* Stream of directives.
* @return Stream of directives.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,6 @@ public final class XmlInstruction implements XmlBytecodeEntry {
);
}

/**
* Constructor.
* @param xml XML string that represents an instruction.
*/
XmlInstruction(final String xml) {
this(new XmlNode(xml));
}

/**
* Constructor.
* @param xmlnode Instruction node.
Expand Down
Loading

1 comment on commit 717f728

@0pdd
Copy link

@0pdd 0pdd commented on 717f728 Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 889-a5a62aec discovered in src/main/java/org/eolang/jeo/representation/XmirRepresentation.java) and submitted as #926. Please, remember that the puzzle was not necessarily added in this particular commit. Maybe it was added earlier, but we discovered it only now.

Please sign in to comment.