Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#1602): discover depends on place #2432

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 61 additions & 41 deletions eo-maven-plugin/src/main/java/org/eolang/maven/DiscoverMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.cactoos.iterable.Filtered;
import org.cactoos.iterable.Mapped;
import org.cactoos.set.SetOf;
import org.eolang.maven.name.ObjectName;
import org.eolang.maven.name.OnDefault;
import org.eolang.maven.name.OnReplaced;
import org.eolang.maven.name.OnSwap;
import org.eolang.maven.name.OnVersioned;
import org.eolang.maven.tojos.ForeignTojo;
import org.eolang.maven.util.Rel;

Expand All @@ -56,27 +58,16 @@ public final class DiscoverMojo extends SafeMojo {
@Override
public void exec() throws FileNotFoundException {
final Collection<ForeignTojo> tojos = this.scopedTojos().notDiscovered();
final Collection<ObjectName> discovered = new HashSet<>(1);
final Collection<String> discovered = new HashSet<>();
for (final ForeignTojo tojo : tojos) {
final Path src = tojo.optimized();
tojo.withDiscovered(
(int) this.discover(src)
.stream()
.filter(name -> !name.isEmpty())
.map(
name -> new OnSwap(
this.withVersions,
new OnReplaced(name, this.hashes)
)
)
.peek(
name -> this.scopedTojos()
.add(name)
.withDiscoveredAt(src)
)
.peek(discovered::add)
.count()
);
final Collection<String> names = this.discover(src, tojo);
discovered.addAll(names);
for (final String name : names) {
this.scopedTojos().add(name).withDiscoveredAt(src);
discovered.add(name);
maxonfjvipon marked this conversation as resolved.
Show resolved Hide resolved
}
tojo.withDiscovered(discovered.size());
}
if (tojos.isEmpty()) {
if (this.scopedTojos().size() == 0) {
Expand All @@ -101,13 +92,14 @@ public void exec() throws FileNotFoundException {
* Pull all deps found in the provided XML file.
*
* @param file The .xmir file
* @param tojo Current tojo.
* @return List of foreign objects found
*/
private Collection<String> discover(final Path file) {
final XML saxon = new SaxonDocument(file);
final Collection<String> names = DiscoverMojo.names(saxon);
if (!saxon.xpath("//o[@vararg]").isEmpty()) {
names.add("org.eolang.tuple");
private Collection<String> discover(final Path file, final ForeignTojo tojo) {
final XML xml = new SaxonDocument(file);
final Collection<String> names = this.names(xml, tojo);
if (!xml.xpath("//o[@vararg]").isEmpty()) {
names.add(this.versioned("org.eolang.tuple", tojo).toString());
}
if (names.isEmpty()) {
Logger.debug(
Expand All @@ -127,28 +119,56 @@ private Collection<String> discover(final Path file) {
* Get a unique list of object names from given XML.
*
* @param xml XML.
* @param tojo Current tojo.
* @return Object names.
*/
private static Set<String> names(final XML xml) {
private Collection<String> names(final XML xml, final ForeignTojo tojo) {
return new SetOf<>(
new Filtered<>(
obj -> !obj.isEmpty(),
xml.xpath(
String.join(
"",
"//o[",
"not(starts-with(@base,'.'))",
" and @base != 'Q'",
" and @base != '^'",
" and @base != '$'",
" and @base != '&'",
" and not(@ref)",
"]/string-join((@base, @ver),'",
OnReplaced.DELIMITER,
"')"
new Mapped<>(
(String name) -> this.versioned(name, tojo).toString(),
new Filtered<>(
name -> !name.isEmpty(),
xml.xpath(
String.join(
"",
"//o[",
"not(starts-with(@base,'.'))",
" and @base != 'Q'",
" and @base != '^'",
" and @base != '$'",
" and @base != '&'",
" and not(@ref)",
"]/string-join((@base,@ver),'",
OnReplaced.DELIMITER,
"')"
)
)
)
)
);
}

/**
* Handle versioning of given object name.
maxonfjvipon marked this conversation as resolved.
Show resolved Hide resolved
*
* @param name Object name with tag on not.
* @param tojo Current tojo.
Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon Is it a tojo? Maybe we can name it as a "default object name", or "default version"?

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo it's an identifier of tojo

Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon But what is the identifier of tojo in this context?

Copy link
Member Author

@maxonfjvipon maxonfjvipon Aug 30, 2023

Choose a reason for hiding this comment

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

@volodya-lombrozo just a name of it as string

Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon You treat it as an object name with hash, or I'm wrong?

Copy link
Member Author

@maxonfjvipon maxonfjvipon Aug 30, 2023

Choose a reason for hiding this comment

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

@volodya-lombrozo with or without. But if I wrap tojo.identifier() into OnDefault somewhere before this function, I'll not be able check if it contains hash or not. The check should happen in this function, that's why I pass tojo String not as ObjectName

* @return Versioned object name.
*/
private ObjectName versioned(final String name, final ForeignTojo tojo) {
maxonfjvipon marked this conversation as resolved.
Show resolved Hide resolved
final String identifier = tojo.identifier();
final ObjectName replaced = new OnReplaced(name, this.hashes);
return new OnSwap(
this.withVersions,
new OnSwap(
identifier.contains(OnReplaced.DELIMITER),
new OnVersioned(
replaced,
new OnDefault(identifier)::hash,
true
),
replaced
)
);
}
}
70 changes: 70 additions & 0 deletions eo-maven-plugin/src/main/java/org/eolang/maven/name/OnDefault.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.maven.name;

import org.eolang.maven.hash.CommitHash;

/**
* Default object name that just split given raw string to name and hash.
*
* @since 0.31.0
*/
public final class OnDefault implements ObjectName {
Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon I believe this change should be done in a separate PR. Please take a look that issue:
#2420

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo I'm not sure in such particular case. OnDefault is just one more ObjectName without complex behaviour.

Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon From the issue description:

Apparently we have to create a class which will parse raw string into two parts value and
optional version.

We have the same split function among many classes (The same we can say about DELIMETER static field). Hence, I believe this issue exactly about this.

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo I suggest to extend the #2420 so it affected this class too

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo I believe adding OnDefault class is not about solving the problem #2420. It's just one more object name. Yes, with code duplication, that will be somehow resolved after solving #2420.

Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon Let's do it for now. But I would recommend to solve #2420 issue first and then return to that PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo yes, let's leave this as it. Object versioning has higher priority than refactoring at the moment


/**
* Raw string.
*/
private final String raw;

/**
* Ctor.
* @param object Object name with hash.
*/
public OnDefault(final String object) {
this.raw = object;
}

@Override
public String value() {
return this.split()[0];
}

@Override
public CommitHash hash() {
return new CommitHash.ChConstant(this.split()[1]);
}

@Override
public String toString() {
return this.raw;
}

/**
* Split raw to name and hash.
* @return Split raw
*/
private String[] split() {
return this.raw.split(String.format("\\%s", OnReplaced.DELIMITER));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package org.eolang.maven.name;

import org.cactoos.Scalar;
import org.cactoos.scalar.Unchecked;
import org.eolang.maven.hash.CommitHash;

Expand All @@ -42,15 +43,34 @@ public final class OnVersioned implements ObjectName {
/**
* Default hash.
*/
private final CommitHash hsh;
private final Unchecked<CommitHash> hsh;
Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon Why do we need this overcomplication? I didn't find any usage of the next constructor:

public OnVersioned(final ObjectName origin, final Scalar<CommitHash> hash) {
    this(new Unchecked<>(origin::toString), new Unchecked<>(hash));
}

And to be honest, I don't see the reason for it.

Copy link
Member Author

@maxonfjvipon maxonfjvipon Aug 30, 2023

Choose a reason for hiding this comment

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

@volodya-lombrozo here. Scalar is needed because explicit new OnDefault(tojo).hash() may fail with IndexOutOfBounds exception if tojo does not contain delimiter |

Copy link
Member

Choose a reason for hiding this comment

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

@maxonfjvipon I see, thank you. Maybe it's better to let it fail as fast as possible? Otherwise it looks like you are trying to swallow the exception or use exceptions for flow control (both of the approaches don't lead to a good design).

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo It should not fail at this step at all. Pay a bit more attention to the code:

new OnSwap(
    tojo.contains(OnReplaced.DELIMITER),
    new OnVersioned(
        replaced,
        new OnDefault(tojo)::hash
    ),
    replaced
)

If condition tojo.contains(OnReplaced.DELIMITER) is FALSE, the first object will be never evaluated, the second one will be - replaced. But we need to construct it here and it should now fail on the construction step

Copy link
Member Author

Choose a reason for hiding this comment

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

@volodya-lombrozo maybe we will add one more object name for such cases, but there's only one for now, so there's no need


/**
* Put default hash forcibly.
*/
private final boolean force;
maxonfjvipon marked this conversation as resolved.
Show resolved Hide resolved

/**
* Ctor.
* @param origin Origin object name
* @param hash Default hash if a version in full name is absent.
*/
public OnVersioned(final ObjectName origin, final CommitHash hash) {
this(new Unchecked<>(origin::toString), hash);
this(origin, () -> hash, false);
}

/**
* Ctor.
* @param origin Origin object name
* @param hash Default hash if a version in full name is absent
* @param force Put a default version forcibly
*/
public OnVersioned(
final ObjectName origin,
final Scalar<CommitHash> hash,
final boolean force
) {
this(new Unchecked<>(origin::toString), new Unchecked<>(hash), force);
}

/**
Expand All @@ -70,17 +90,23 @@ public OnVersioned(final String object, final String hash) {
* @param def Default hash if a version in full name is absent.
*/
public OnVersioned(final String object, final CommitHash def) {
this(new Unchecked<>(() -> object), def);
this(new Unchecked<>(() -> object), new Unchecked<>(() -> def), false);
}

/**
* Ctor.
* @param object Object full name with a version or not as scalar.
* @param def Default hash if a version in full name is absent.
* @param force Put a default version forcibly.
*/
private OnVersioned(final Unchecked<String> object, final CommitHash def) {
private OnVersioned(
final Unchecked<String> object,
final Unchecked<CommitHash> def,
final boolean force
) {
this.object = object;
this.hsh = def;
this.force = force;
}

@Override
Expand Down Expand Up @@ -108,8 +134,8 @@ public String toString() {
*/
private String[] split() {
String[] splt = this.object.value().split(String.format("\\%s", OnReplaced.DELIMITER));
if (splt.length == 1) {
splt = new String[]{splt[0], this.hsh.value()};
if (splt.length == 1 || this.force) {
splt = new String[]{splt[0], this.hsh.value().value()};
}
return splt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import com.yegor256.tojos.MnCsv;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
Expand All @@ -35,6 +37,7 @@
import org.eolang.maven.hash.CommitHash;
import org.eolang.maven.hash.CommitHashesMap;
import org.eolang.maven.name.ObjectName;
import org.eolang.maven.name.OnDefault;
import org.eolang.maven.name.OnVersioned;
import org.eolang.maven.tojos.ForeignTojo;
import org.eolang.maven.tojos.ForeignTojos;
Expand Down Expand Up @@ -174,6 +177,52 @@ void discoversWithSeveralObjectsWithDifferentVersions(
);
}

@Test
void discoversDifferentUnversionedObjectsFromDifferentVersionedObjects(@TempDir final Path tmp)
throws IOException {
final Map<String, CommitHash> hashes = new CommitHashesMap.Fake();
final String program = String.join("\n", "[] > sprintf", " \"Hello world\" > @");
final String one = hashes.get("0.28.1").value();
final String two = hashes.get("0.28.2").value();
final String string = "org.eolang.string";
final String sprintf = "foo/x/sprintf";
final String object = "foo.x.sprintf";
final String ext = "%s.eo";
final String format = String.join("_", "%s", ext);
final ForeignTojos tojos = new FakeMaven(tmp)
.with("withVersions", true)
.withProgram(
Paths.get(String.format(format, sprintf, one)),
program,
new OnVersioned(object, one)
)
.withProgram(
Paths.get(String.format(format, sprintf, two)),
maxonfjvipon marked this conversation as resolved.
Show resolved Hide resolved
program,
new OnVersioned(object, two)
)
.withProgram(
Paths.get(String.format(ext, sprintf)),
program,
new OnDefault(object)
)
.execute(new FakeMaven.Discover())
.externalTojos();
MatcherAssert.assertThat(
maxonfjvipon marked this conversation as resolved.
Show resolved Hide resolved
String.format(
"Tojos should contained 3 similar objects %s: 2 with different hashes %s and one without; but they didn't",
string,
Arrays.toString(new String[]{one, two})
),
tojos.contains(
new OnVersioned(string, one),
new OnVersioned(string, two),
new OnDefault(string)
),
Matchers.is(true)
);
}

@Test
void doesNotDiscoverWithVersions(@TempDir final Path tmp) throws IOException {
final FakeMaven maven = new FakeMaven(tmp)
Expand Down
Loading
Loading