diff --git a/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java b/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java index f808fbfc94..5f69e64983 100644 --- a/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java +++ b/eo-maven-plugin/src/main/java/org/eolang/maven/optimization/OptCached.java @@ -41,12 +41,19 @@ * Returns already optimized XML if it's found in the cache. * * @since 0.28.11 - * @todo #2746:30min Use checksum, not time. + * @todo #2746:30min Fix caching mechanism in {@link OptCached}. Current + * The last modified time of the files between stages may be different, + * so it is not correct to do an equality comparison ({@code .equals(...)}). + * The last modification time of the file at the current stage + * must be less than or equal to the last modification time of file in cache at the next stage. * The following tests show that fetching from the cache doesn't work correctly: * - {@link OptCachedTest#returnsFromCacheCorrectProgram(Path path)}, * - {@link OptCachedTest#returnsFromCacheButTimesSaveAndExecuteDifferent(Path path)}. - * Need to fix the file validation from cache: using checksum, but not time. * Don't forget to enable the tests. + * @todo #2746:30min Unify caching mechanism on stages: parse, optimize, pull and so on. + * Current implementations of caching on parsing stage and optimize stages work differently. + * In ParseMojo we have condition {@code if (tojo.hasHash()) }, in OptimizeMojo or ShakeMojo we + * compare creation time of files. */ public final class OptCached implements Optimization { diff --git a/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java b/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java index 6200d5f8d1..825354b85f 100644 --- a/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java +++ b/eo-parser/src/main/java/org/eolang/parser/EoSyntax.java @@ -26,6 +26,7 @@ import com.jcabi.log.Logger; import com.jcabi.xml.XML; import com.jcabi.xml.XMLDocument; +import com.yegor256.xsline.Xsline; import java.io.IOException; import java.util.List; import org.antlr.v4.runtime.CommonTokenStream; @@ -70,7 +71,7 @@ public EoSyntax(final String nme, final Input ipt) { } /** - * Compile it to XML and save. + * Compile it to XML. * *
No exception will be thrown if the syntax is invalid. In any case, XMIR will
* be generated and saved. Read it in order to find the errors,
@@ -92,10 +93,12 @@ public XML parsed() throws IOException {
parser.addErrorListener(spy);
final XeEoListener xel = new XeEoListener(this.name);
new ParseTreeWalker().walk(xel, parser.program());
- final XML dom = new XMLDocument(
- new Xembler(
- new Directives(xel).append(spy)
- ).domQuietly()
+ final XML dom = new Xsline(new StHash()).pass(
+ new XMLDocument(
+ new Xembler(
+ new Directives(xel).append(spy)
+ ).domQuietly()
+ )
);
new Schema(dom).check();
if (spy.size() == 0) {
diff --git a/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java b/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java
index c06438cdd0..79a7265632 100644
--- a/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java
+++ b/eo-parser/src/main/java/org/eolang/parser/PhiSyntax.java
@@ -26,6 +26,7 @@
import com.jcabi.log.Logger;
import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
+import com.yegor256.xsline.Xsline;
import java.io.IOException;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
@@ -84,10 +85,12 @@ public XML parsed() throws IOException {
parser.removeErrorListeners();
parser.addErrorListener(spy);
new ParseTreeWalker().walk(xel, parser.program());
- final XML dom = new XMLDocument(
- new Xembler(
- new Directives(xel).append(spy)
- ).domQuietly()
+ final XML dom = new Xsline(new StHash()).pass(
+ new XMLDocument(
+ new Xembler(
+ new Directives(xel).append(spy)
+ ).domQuietly()
+ )
);
new Schema(dom).check();
if (spy.size() == 0) {
diff --git a/eo-parser/src/main/java/org/eolang/parser/StHash.java b/eo-parser/src/main/java/org/eolang/parser/StHash.java
new file mode 100644
index 0000000000..671e1ebaf7
--- /dev/null
+++ b/eo-parser/src/main/java/org/eolang/parser/StHash.java
@@ -0,0 +1,119 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2023 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.jcabi.xml.XML;
+import com.jcabi.xml.XMLDocument;
+import com.yegor256.xsline.StEnvelope;
+import com.yegor256.xsline.StLambda;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import org.xembly.Directives;
+import org.xembly.Xembler;
+
+/**
+ * Add attribute 'hash' in node 'program' in XML.
+ * Returns already XML with hash.
+ *
+ * @since 0.35.0
+ *
+ * @todo #2764:30min Replace the hash code entry
+ * from the XML file node attribute to the file attribute.
+ * This is necessary to increase the speed of searching for a cached file.
+ * The hash code entry should be deleted from EoSyntax.java and PhiSyntax.java.
+ * Hash code should save to the file attribute in ParseMojo.java and UnphiMojo.
+ * Delete tests containsHash() from EoSyntaxTest.java and PhiSyntaxTest.java.
+ * @todo #2764:30min Is it possible set the hash function directly
+ * in XeEoListener during the parsing? Now hash code is generated in class StHash.java.
+ * Maybe it will be better generating hash code in class XeEoListener.
+ */
+public final class StHash extends StEnvelope {
+
+ /**
+ * Returns XML with attribute 'hash'.
+ */
+ public StHash() {
+ super(
+ new StLambda((
+ position,
+ xml) ->
+ new XMLDocument(
+ new Xembler(
+ new Directives().xpath("//program").attr("hash", new Hash(xml).compute())
+ ).apply(xml.node())
+ )
+ )
+ );
+ }
+
+ /**
+ * Hash is generated by MD5 using node '/program/objects'.
+ * Returns program hash.
+ *
+ * @since 0.35.0
+ */
+ public static final class Hash {
+
+ /**
+ * XML for which the hash code will be made.
+ */
+ private final XML xml;
+
+ /**
+ * The constructor.
+ *
+ * @param xml XML for which the hash code will be made.
+ */
+ public Hash(final XML xml) {
+ this.xml = xml;
+ }
+
+ /**
+ * Return program hash using node "/program/objects".
+ *
+ * @return String hash of this XML.
+ * @throws NoSuchAlgorithmException If fails.
+ */
+ public String compute() throws NoSuchAlgorithmException {
+ final BigInteger number = new BigInteger(
+ 1,
+ MessageDigest
+ .getInstance("MD5")
+ .digest(
+ this.xml.nodes(
+ "/program/objects"
+ )
+ .toString().getBytes()
+ )
+ );
+ final StringBuilder hash = new StringBuilder(number.toString(16));
+ while (hash.length() < 32) {
+ hash.insert(0, "0");
+ }
+ return hash.toString();
+ }
+ }
+}
+
diff --git a/eo-parser/src/main/resources/XMIR.xsd b/eo-parser/src/main/resources/XMIR.xsd
index 2032196dc3..6a7e95ff67 100644
--- a/eo-parser/src/main/resources/XMIR.xsd
+++ b/eo-parser/src/main/resources/XMIR.xsd
@@ -113,6 +113,7 @@ SOFTWARE.