diff --git a/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyInfo.java b/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyInfo.java index e80b358d..fac62393 100644 --- a/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyInfo.java +++ b/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyInfo.java @@ -1,26 +1,15 @@ -/* - * Copyright 2019 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.gestalt.module.dependencyresolution; import org.terasology.gestalt.naming.Name; +import org.terasology.gestalt.naming.SemverExpression; import org.terasology.gestalt.naming.Version; import org.terasology.gestalt.naming.VersionRange; import java.util.Objects; +import java.util.function.Predicate; /** * Describes a dependency on a module. Dependencies apply to a range of versions - anything from the min version (inclusive) to the max version (exclusive) are supported. @@ -126,6 +115,21 @@ public VersionRange versionRange() { return new VersionRange(getMinVersion(), getMaxVersion()); } + public Predicate versionPredicate() { + if (maxVersion != null) { + return versionRange(); + } else { + String source; + if (minVersion.isSnapshot()) { + // semver doesn't like snapshots in caret ranges? + source = minVersion + " | ^" + minVersion.getNextPatchVersion(); + } else { + source = "^" + minVersion; + } + return new SemverExpression(source); + } + } + @Override public String toString() { return String.format("DependencyInfo [id=%s, minVersion=%s, maxVersion=%s, optional=%s]", diff --git a/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyResolver.java b/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyResolver.java index a3de6039..1f207b7d 100644 --- a/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyResolver.java +++ b/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/DependencyResolver.java @@ -1,18 +1,5 @@ -/* - * Copyright 2019 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.gestalt.module.dependencyresolution; @@ -25,6 +12,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Optional; +import java.util.function.Predicate; /** * Dependency Resolver determines a working set of modules for a given set of desired modules. Where multiple versions are compatible, they are resolved in favour of the @@ -86,7 +74,7 @@ public ResolutionBuilder builder() { * Prepares and performs the process of resolving dependencies. */ public class ResolutionBuilder { - private final Map> validVersions = new HashMap<>(); + private final Map>> validVersions = new HashMap<>(); /** * Adds a module to the set of requirements. diff --git a/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/ResolutionAttempt.java b/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/ResolutionAttempt.java index fd5326aa..ead6627b 100644 --- a/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/ResolutionAttempt.java +++ b/gestalt-module/src/main/java/org/terasology/gestalt/module/dependencyresolution/ResolutionAttempt.java @@ -1,18 +1,5 @@ -/* - * Copyright 2019 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.gestalt.module.dependencyresolution; @@ -30,7 +17,6 @@ import org.terasology.gestalt.module.ModuleRegistry; import org.terasology.gestalt.naming.Name; import org.terasology.gestalt.naming.Version; -import org.terasology.gestalt.naming.VersionRange; import org.terasology.gestalt.util.collection.UniqueQueue; import java.util.Arrays; @@ -42,6 +28,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; class ResolutionAttempt { @@ -58,7 +45,7 @@ class ResolutionAttempt { this.optionalStrategy = optionalStrategy; } - ResolutionResult resolve(Map> validVersions) { + ResolutionResult resolve(Map>> validVersions) { rootModules = ImmutableSet.copyOf(validVersions.keySet()); populateDomains(validVersions); populateConstraints(); @@ -80,7 +67,7 @@ ResolutionResult resolve(Map> validVersions) { /** * Populates the domains (modules of interest) for resolution. Includes all versions of all modules depended on by any version of a module of interest, recursively. */ - private void populateDomains(Map> validVersions) { + private void populateDomains(Map>> validVersions) { moduleVersionPool = HashMultimap.create(); Set involvedModules = Sets.newHashSet(); Deque moduleQueue = Queues.newArrayDeque(); @@ -92,8 +79,8 @@ private void populateDomains(Map> validVersions) { while (!moduleQueue.isEmpty()) { Name id = moduleQueue.pop(); for (Module version : registry.getModuleVersions(id)) { - Optional range = validVersions.getOrDefault(version.getId(), Optional.empty()); - if (!range.isPresent() || range.get().contains(version.getVersion())) { + Optional> range = validVersions.getOrDefault(version.getId(), Optional.empty()); + if (!range.isPresent() || range.get().test(version.getVersion())) { moduleVersionPool.put(id, new PossibleVersion(version.getVersion())); for (DependencyInfo dependency : version.getMetadata().getDependencies()) { if (involvedModules.add(dependency.getId())) { @@ -125,7 +112,7 @@ private void populateConstraints() { Module versionedModule = registry.getModule(name, version.getVersion().get()); DependencyInfo info = versionedModule.getMetadata().getDependencyInfo(dependency); if (info != null) { - constraintTable.put(version.getVersion().get(), new CompatibleVersions(info.versionRange(), info.isOptional() && !optionalStrategy.isRequired())); + constraintTable.put(version.getVersion().get(), new CompatibleVersions(info.versionPredicate(), info.isOptional() && !optionalStrategy.isRequired())); } } } @@ -376,17 +363,17 @@ public String toString() { } private static class CompatibleVersions { - private final VersionRange versionRange; + private final Predicate versionRange; private final boolean missingAllowed; - public CompatibleVersions(VersionRange versionRange, boolean missingAllowed) { + public CompatibleVersions(Predicate versionRange, boolean missingAllowed) { this.versionRange = versionRange; this.missingAllowed = missingAllowed; } public boolean isCompatible(PossibleVersion version) { if (version.getVersion().isPresent()) { - return versionRange.contains(version.getVersion().get()); + return versionRange.test(version.getVersion().get()); } else { return missingAllowed; } diff --git a/gestalt-module/src/main/java/org/terasology/gestalt/naming/SemverExpression.java b/gestalt-module/src/main/java/org/terasology/gestalt/naming/SemverExpression.java new file mode 100644 index 00000000..9c206571 --- /dev/null +++ b/gestalt-module/src/main/java/org/terasology/gestalt/naming/SemverExpression.java @@ -0,0 +1,32 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.gestalt.naming; + +import com.github.zafarkhaja.semver.expr.ExpressionParser; +import com.google.common.base.MoreObjects; + +import java.util.function.Predicate; + +public class SemverExpression implements Predicate { + + private final Predicate expression; + private final String source; + + public SemverExpression(String source) { + this.source = source; + this.expression = ExpressionParser.newInstance().parse(source); + } + + @Override + public boolean test(Version version) { + return expression.test(version.semver); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .addValue(source) + .toString(); + } +} diff --git a/gestalt-module/src/main/java/org/terasology/gestalt/naming/Version.java b/gestalt-module/src/main/java/org/terasology/gestalt/naming/Version.java index 56cbaff3..d0029bf8 100644 --- a/gestalt-module/src/main/java/org/terasology/gestalt/naming/Version.java +++ b/gestalt-module/src/main/java/org/terasology/gestalt/naming/Version.java @@ -1,18 +1,5 @@ -/* - * Copyright 2019 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.gestalt.naming; import com.github.zafarkhaja.semver.ParseException; @@ -32,7 +19,7 @@ public final class Version implements Comparable { private static final String SNAPSHOT = "SNAPSHOT"; - private com.github.zafarkhaja.semver.Version semver; + com.github.zafarkhaja.semver.Version semver; /** * Constructs a version with the given values diff --git a/gestalt-module/src/main/java/org/terasology/gestalt/naming/VersionRange.java b/gestalt-module/src/main/java/org/terasology/gestalt/naming/VersionRange.java index 8c8f04ce..fa22ad38 100644 --- a/gestalt-module/src/main/java/org/terasology/gestalt/naming/VersionRange.java +++ b/gestalt-module/src/main/java/org/terasology/gestalt/naming/VersionRange.java @@ -1,31 +1,19 @@ -/* - * Copyright 2019 MovingBlocks - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 package org.terasology.gestalt.naming; import com.google.common.base.Preconditions; import java.util.Objects; +import java.util.function.Predicate; /** * A range of versions from a lower-bound (inclusive) to an upper-bound (exclusive). * * @author Immortius */ -public class VersionRange { +public class VersionRange implements Predicate { private final Version lowerBound; private final Version upperBound; @@ -59,6 +47,11 @@ public boolean contains(Version version) { return version.compareTo(lowerBound.getSnapshot()) >= 0 && version.compareTo(upperBound.getSnapshot()) < 0; } + @Override + public boolean test(Version version) { + return contains(version); + } + @Override public boolean equals(Object obj) { if (obj == this) {