From d57f98311c9f7249d0d913399c2d9ae913923df8 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 28 Aug 2024 13:07:50 +0200 Subject: [PATCH 01/82] Remove unused PrintMavenAsCycloneDxBom (#4444) --- .../utilities/PrintMavenAsCycloneDxBom.java | 226 ------------------ .../PrintMavenAsCycloneDxBomTest.java | 170 ------------- 2 files changed, 396 deletions(-) delete mode 100755 rewrite-maven/src/main/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBom.java delete mode 100755 rewrite-maven/src/test/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBomTest.java diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBom.java deleted file mode 100755 index 897f463b033..00000000000 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBom.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2020 the original author or authors. - *

- * 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 - *

- * https://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. - */ -package org.openrewrite.maven.utilities; - -import org.openrewrite.internal.ListUtils; -import org.openrewrite.maven.tree.*; -import org.openrewrite.xml.tree.Xml; - -import java.time.Instant; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Print the dependency graph in the CycloneDX (https://cyclonedx.org/) bill of materials (BOM) format. - */ -public final class PrintMavenAsCycloneDxBom { - - private PrintMavenAsCycloneDxBom() { - } - - public static String print(Xml.Document maven) { - - MavenResolutionResult resolutionResult = maven.getMarkers().findFirst(MavenResolutionResult.class) - .orElseThrow(() -> new IllegalStateException("Expected to find a maven resolution marker")); - - ResolvedPom pom = resolutionResult.getPom(); - - StringBuilder bom = new StringBuilder("\n"); - bom.append("\n"); - writeMetadata(pom, bom); - - List compileScopeDependencies = resolutionResult.getDependencies().get(Scope.Compile); - List providedScopeDependencies = resolutionResult.getDependencies().get(Scope.Provided); - - if (providedScopeDependencies != null && !providedScopeDependencies.isEmpty()) { - //Filter out duplicate group/artifacts that already exist in compile scope - Set artifacts = compileScopeDependencies.stream().map(PrintMavenAsCycloneDxBom::dependencyToGroupArtifact).collect(Collectors.toSet()); - providedScopeDependencies = providedScopeDependencies.stream().filter(d -> !artifacts.contains(PrintMavenAsCycloneDxBom.dependencyToGroupArtifact(d))).collect(Collectors.toList()); - } - - //May need to do more dependencies (in the various scopes) - writeComponents(compileScopeDependencies, providedScopeDependencies, bom); - writeDependencies(ListUtils.concatAll(compileScopeDependencies, providedScopeDependencies), bom); - - bom.append("\n"); - - return bom.toString(); - } - - private static GroupArtifact dependencyToGroupArtifact(ResolvedDependency dependency) { - return new GroupArtifact(dependency.getGroupId(), dependency.getArtifactId()); - } - - private static void writeMetadata(ResolvedPom pom, StringBuilder bom) { - bom.append(" \n"); - bom.append(" ").append(Instant.now().toString()).append("\n"); - bom.append(" \n"); - bom.append(" \n"); - bom.append(" OpenRewrite\n"); - bom.append(" OpenRewrite CycloneDX\n"); - //Probably should pull the version from build properties. - bom.append(" 7.18.0\n"); - bom.append(" \n"); - bom.append(" \n"); - - //(Scope scope, String groupId, String artifactId, String version, String packaging, List licenses, String bomReference, StringBuilder bom) { - String packaging = ("war".equals(pom.getPackaging()) || "ear".equals(pom.getPackaging())) ? "application" : "library"; - writeComponent( - Scope.Compile, - pom.getValue(pom.getGroupId()), - pom.getArtifactId(), - pom.getValue(pom.getVersion()), - packaging, - pom.getPackaging(), - pom.getRequested().getLicenses(), - bom); - - bom.append(" \n"); - } - - private static void writeComponents(List dependencies, List provided, StringBuilder bom) { - if (dependencies.isEmpty()) { - return; - } - - bom.append(" \n"); - for (ResolvedDependency dependency : dependencies) { - writeComponent( - Scope.Compile, - dependency.getGroupId(), - dependency.getArtifactId(), - dependency.getVersion(), - "library", - "jar", - dependency.getLicenses(), - bom); - } - for (ResolvedDependency dependency : provided) { - writeComponent( - Scope.Provided, - dependency.getGroupId(), - dependency.getArtifactId(), - dependency.getVersion(), - "library", - "jar", - dependency.getLicenses(), - bom); - } - bom.append(" \n"); - } - private static void writeDependencies(List dependencies, StringBuilder bom) { - if (dependencies.isEmpty()) { - return; - } - bom.append(" \n"); - for (ResolvedDependency dependency : dependencies) { - writeDependency(dependency, bom); - } - bom.append(" \n"); - } - - private static void writeDependency(ResolvedDependency dependency, StringBuilder bom) { - String bomReference = getBomReference(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), "jar"); - bom.append(" \n"); - if (dependency.getDependencies() != null) { - for (ResolvedDependency nested : dependency.getDependencies()) { - bom.append(" \n"); - } - } - bom.append(" \n"); - } - - private static void writeComponent(Scope scope, String groupId, String artifactId, String version, - String packaging, String mavenPackaging, List licenses, StringBuilder bom) { - - String indent = " "; - String bomReference = getBomReference(groupId, artifactId, version, mavenPackaging); - bom.append(indent).append("\n"); - bom.append(indent).append(" ").append(groupId).append("\n"); - bom.append(indent).append(" ").append(artifactId).append("\n"); - bom.append(indent).append(" ").append(version).append("\n"); - - if (scope != null) { - //Cyclone schema allows three scopes: - String cycloneScope; - switch (scope) { - case Compile: - case System: - cycloneScope = "required"; - break; - case None: - case Invalid: - case Test: - cycloneScope = "excluded"; - break; - default: - cycloneScope = "optional"; - } - bom.append(indent).append(" ").append(cycloneScope).append("\n"); - } - writeLicenses(licenses, bom, indent); - bom.append(indent).append(" ").append(bomReference).append("\n"); - bom.append(indent).append("\n"); - } - - private static void writeLicenses(List licenses, StringBuilder bom, String indent) { - - if (!licenses.isEmpty()) { - bom.append(indent).append(" \n"); - - for (License license : licenses) { - bom.append(indent).append(" \n"); - String spdxId = null; - - //This logic maps the rewrite license type to the spdx equivalent. - - //The only license type that we can establish unambiguously is the Apache 2.0 license. - - //BSD has several SPDX Mappings (no way to resolve this) - //CDDL has a v1.0 and v1.1 (we do not distinguish them) - //CreativeCommons has several SPDX Mappings (no way to resolve this) - //Eclipse has a v1.0 and v2.0 (we do not distinguish them) - //GPL has several SPDX Mappings (no way to resolve this) - //LGPL has several SPDX Mappings (no way to resolve this) - //MIT has several SPDX Mappings (no way to resolve this) - //Mozilla has several SPDX Mappings (no way to resolve this) - //PublicDomain unclear which ID to use. - - if (license.getType() == License.Type.Apache2) { - spdxId = "Apache-2.0"; - } - if (spdxId != null) { - bom.append(indent).append(" ").append(spdxId).append("\n"); - } - bom.append(indent).append(" ").append(license.getName()).append("\n"); - bom.append(indent).append(" \n"); - } - bom.append(indent).append(" \n"); - } - } - - private static String getBomReference(String group, String artifactId, String version, String mavenPackaging) { - return "pkg:maven/" + group + "/" + artifactId + "@" + version + "?type=" + mavenPackaging; - } -} diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBomTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBomTest.java deleted file mode 100755 index 0731dd68a17..00000000000 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/utilities/PrintMavenAsCycloneDxBomTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2020 the original author or authors. - *

- * 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 - *

- * https://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. - */ -package org.openrewrite.maven.utilities; - -import org.junit.jupiter.api.Test; -import org.openrewrite.maven.MavenParser; -import org.openrewrite.test.RewriteTest; -import org.openrewrite.xml.tree.Xml; - -import static org.assertj.core.api.Assertions.assertThat; - -class PrintMavenAsCycloneDxBomTest implements RewriteTest { - - @Test - void cycloneDxBom() { - Xml.Document pom = (Xml.Document) MavenParser.builder() - .build() - .parse( - """ - - 4.0.0 - \s - com.mycompany.app - my-app - 1 - \s - - - org.yaml - snakeyaml - 1.27 - - - org.junit.jupiter - junit-jupiter - 5.7.0 - test - - - - """ - ).toList().get(0); - - String bom = PrintMavenAsCycloneDxBom.print(pom) - .replaceAll(".*", "TODAY"); - - assertThat(bom).isEqualTo(String.format( - """ - - - - TODAY - - - OpenRewrite - OpenRewrite CycloneDX - 7.18.0 - - - - com.mycompany.app - my-app - 1 - required - pkg:maven/com.mycompany.app/my-app@1?type=jar - - - - - org.yaml - snakeyaml - 1.27 - required - - - Apache-2.0 - Apache License, Version 2.0 - - - pkg:maven/org.yaml/snakeyaml@1.27?type=jar - - - - - - - - """, pom.getId().toString()) - ); - - } - - @Test - void pomPackaging_cycloneDxBom() { - Xml.Document pom = (Xml.Document) MavenParser.builder() - .build() - .parse( - """ - - - 4.0.0 - - org.example - pom_packaging - 1.0 - pom - - - 11 - 11 - - - - - - org.junit.jupiter - junit-jupiter-api - 5.9.3 - test - - - - """ - ).toList().get(0); - - String bom = PrintMavenAsCycloneDxBom.print(pom) - .replaceAll(".*", "TODAY"); - - assertThat(bom).isEqualTo(String.format( - """ - - - - TODAY - - - OpenRewrite - OpenRewrite CycloneDX - 7.18.0 - - - - org.example - pom_packaging - 1.0 - required - pkg:maven/org.example/pom_packaging@1.0?type=pom - - - - """, pom.getId().toString()) - ); - - } -} From 7dadcb1788f9cdc1c81322cb2365ad825059356f Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 28 Aug 2024 11:21:37 -0700 Subject: [PATCH 02/82] AddOrUpdateChild should only format the new element, not the parent of the new element --- .../maven/ChangePluginConfigurationTest.java | 14 +++++++------- .../maven/ChangePluginDependenciesTest.java | 12 ++++++------ .../maven/ChangePluginExecutionsTest.java | 12 ++++++------ .../java/org/openrewrite/xml/AddOrUpdateChild.java | 3 +-- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginConfigurationTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginConfigurationTest.java index b8ed52c4d92..49a8d12c8af 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginConfigurationTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginConfigurationTest.java @@ -33,7 +33,7 @@ void removeConfiguration() { org.example foo 1.0 - + @@ -55,7 +55,7 @@ void removeConfiguration() { org.example foo 1.0 - + @@ -84,7 +84,7 @@ void addConfiguration() { org.example foo 1.0 - + @@ -101,7 +101,7 @@ void addConfiguration() { org.example foo 1.0 - + @@ -135,7 +135,7 @@ void replaceConfiguration() { org.example foo 1.0 - + @@ -153,7 +153,7 @@ void replaceConfiguration() { org.example foo 1.0 - + @@ -187,7 +187,7 @@ void transformConfigurationNoOpWhenConfigurationMissing() { org.example foo 1.0 - + diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginDependenciesTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginDependenciesTest.java index 845e4675691..e0cfad390cb 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginDependenciesTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginDependenciesTest.java @@ -34,7 +34,7 @@ void removeDependencies() { org.example foo 1.0 - + @@ -58,7 +58,7 @@ void removeDependencies() { org.example foo 1.0 - + @@ -87,7 +87,7 @@ void addDependencies() { org.example foo 1.0 - + @@ -104,7 +104,7 @@ void addDependencies() { org.example foo 1.0 - + @@ -140,7 +140,7 @@ void replaceDependencies() { org.example foo 1.0 - + @@ -158,7 +158,7 @@ void replaceDependencies() { org.example foo 1.0 - + diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginExecutionsTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginExecutionsTest.java index 88acc996965..01aefec9af5 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginExecutionsTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/ChangePluginExecutionsTest.java @@ -34,7 +34,7 @@ void removeExecutions() { org.example foo 1.0 - + @@ -57,7 +57,7 @@ void removeExecutions() { org.example foo 1.0 - + @@ -86,7 +86,7 @@ void addExecutions() { org.example foo 1.0 - + @@ -103,7 +103,7 @@ void addExecutions() { org.example foo 1.0 - + @@ -140,7 +140,7 @@ void replaceExecutions() { org.example foo 1.0 - + @@ -158,7 +158,7 @@ void replaceExecutions() { org.example foo 1.0 - + diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/AddOrUpdateChild.java b/rewrite-xml/src/main/java/org/openrewrite/xml/AddOrUpdateChild.java index bfc6ca50678..a83ee924e88 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/AddOrUpdateChild.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/AddOrUpdateChild.java @@ -42,11 +42,10 @@ public Xml visitTag(Xml.Tag tag, P p) { } t = mapChildren(t, it -> { if (it == maybeChild.get()) { - return child.withPrefix(maybeChild.get().getPrefix()); + return autoFormat(child.withPrefix(maybeChild.get().getPrefix()), p, getCursor()); } return it; }); - t = autoFormat(t, p, getCursor().getParentOrThrow()); } else { t = addToTag(t, child, getCursor().getParentOrThrow()); } From b64e1ed56584a03173c55abb831208af6e95afa3 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Thu, 29 Aug 2024 11:39:00 +0200 Subject: [PATCH 03/82] Custom `distributionUrl` in gradle properties should be upgraded in place (#4445) * Add testcase that uses an existing custom distribution url when upgrading gradle wrapper * Refactor test * If a specific version is provided but no wrapper url we reuse the existing distribution url and update the version assuming the user provided a version they know exists. * Add gradle zip using lfs * Trim gradle zip and re-add * Trim and re-add gradle zip again * Trim Gradle 8.10 zip from 20MB to 200kB for tests * Minor fixes * Update rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review * Tolerate customized Gradle versions after feedback * Refactoring to apply ExactVersion before even checking services.gradle.org, taking any provided wrapperUri into account * Revert "Refactoring to apply ExactVersion before even checking services.gradle.org, taking any provided wrapperUri into account" This reverts commit 8532393fa56c733c267b8237b3c813e062d48a3c. * Prevent null warnings with same workaround as before * Apply formatter * NullOrEmpty check on version * Consistently use `isBlank` and trim test --------- Co-authored-by: Laurens Westerlaken Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../gradle/UpdateGradleWrapper.java | 34 +++-- .../gradle/util/GradleWrapper.java | 2 +- .../gradle/UpdateGradleWrapperTest.java | 126 ++++++++++++------ .../src/test/resources/gradle-8.10-bin.zip | Bin 0 -> 284691 bytes 4 files changed, 109 insertions(+), 53 deletions(-) create mode 100644 rewrite-gradle/src/test/resources/gradle-8.10-bin.zip diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java index 887c0b9a502..67ba93dbf15 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateGradleWrapper.java @@ -24,6 +24,7 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.gradle.search.FindGradleProject; +import org.openrewrite.gradle.util.DistributionInfos; import org.openrewrite.gradle.util.GradleWrapper; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.StringUtils; @@ -35,6 +36,7 @@ import org.openrewrite.properties.tree.Properties; import org.openrewrite.quark.Quark; import org.openrewrite.remote.Remote; +import org.openrewrite.semver.ExactVersion; import org.openrewrite.semver.Semver; import org.openrewrite.semver.VersionComparator; import org.openrewrite.text.PlainText; @@ -127,9 +129,15 @@ private GradleWrapper getGradleWrapper(ExecutionContext ctx) { // services.gradle.org is unreachable, possibly because of a firewall // But if the user specified a wrapperUri to an internal repository things might still be workable if (wrapperUri == null) { - throw new IllegalArgumentException( - "Could not reach services.gradle.org and no alternative wrapper URI is provided. " + - "To use this recipe in environments where services.gradle.org is unavailable specify a wrapperUri.", e); + // If the user didn't specify a wrapperUri, but they did provide a specific version we assume they know this version + // is available from whichever distribution url they were previously using and update the version + if (!StringUtils.isBlank(version) && Semver.validate(version, null).getValue() instanceof ExactVersion) { + return gradleWrapper = new GradleWrapper(version, new DistributionInfos("", null, null)); + } else { + throw new IllegalArgumentException( + "Could not reach services.gradle.org, no alternative wrapper URI is provided and no exact version is provided. " + + "To use this recipe in environments where services.gradle.org is unavailable specify a wrapperUri or exact version.", e); + } } if (wrapperUri.contains("${version})")) { if (version == null) { @@ -154,7 +162,10 @@ private GradleWrapper getGradleWrapper(ExecutionContext ctx) { public static class GradleWrapperState { boolean gradleProject = false; boolean needsWrapperUpdate = false; + + @Nullable BuildTool updatedMarker; + boolean addGradleWrapperProperties = true; boolean addGradleWrapperJar = true; boolean addGradleShellScript = true; @@ -191,14 +202,14 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) { return false; } - GradleWrapper gradleWrapper = getGradleWrapper(ctx); + String gradleWrapperVersion = getGradleWrapper(ctx).getVersion(); VersionComparator versionComparator = requireNonNull(Semver.validate(isBlank(version) ? "latest.release" : version, null).getValue()); - int compare = versionComparator.compare(null, buildTool.getVersion(), gradleWrapper.getVersion()); + int compare = versionComparator.compare(null, buildTool.getVersion(), gradleWrapperVersion); // maybe we want to update the distribution type or url if (compare < 0) { acc.needsWrapperUpdate = true; - acc.updatedMarker = buildTool.withVersion(gradleWrapper.getVersion()); + acc.updatedMarker = buildTool.withVersion(gradleWrapperVersion); return true; } else { return compare == 0; @@ -211,10 +222,17 @@ public Properties visitEntry(Properties.Entry entry, ExecutionContext ctx) { return entry; } - GradleWrapper gradleWrapper = getGradleWrapper(ctx); - // Typical example: https://services.gradle.org/distributions/gradle-7.4-all.zip String currentDistributionUrl = entry.getValue().getText(); + + GradleWrapper gradleWrpr = getGradleWrapper(ctx); + if (StringUtils.isBlank(gradleWrpr.getDistributionUrl()) && + !StringUtils.isBlank(version) && Semver.validate(version, null).getValue() instanceof ExactVersion) { + String newDownloadUrl = currentDistributionUrl.replace("\\", "") + .replaceAll("(.*gradle-)(\\d+\\.\\d+(?:\\.\\d+)?)(.*-(?:bin|all).zip)", "$1" + gradleWrapper.getVersion() + "$3"); + gradleWrapper = new GradleWrapper(version, new DistributionInfos(newDownloadUrl, null, null)); + } + if (!gradleWrapper.getPropertiesFormattedUrl().equals(currentDistributionUrl)) { acc.needsWrapperUpdate = true; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/GradleWrapper.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/GradleWrapper.java index 6459cc8588d..806b1a13b75 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/GradleWrapper.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/GradleWrapper.java @@ -102,7 +102,7 @@ public static GradleWrapper create(@Nullable String distributionTypeName, @Nulla public static GradleWrapper create(URI fullDistributionUri, @SuppressWarnings("unused") ExecutionContext ctx) { String version = ""; Matcher matcher = GRADLE_VERSION_PATTERN.matcher(fullDistributionUri.toString()); - if(matcher.find()) { + if (matcher.find()) { version = matcher.group(1); } return new GradleWrapper(version, new DistributionInfos(fullDistributionUri.toString(), null, null)); diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java index 91f8b6b85cf..dd798643531 100755 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java @@ -43,14 +43,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.gradle.Assertions.buildGradle; import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_BATCH_LOCATION; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_JAR_LOCATION; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_PROPERTIES_LOCATION; -import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_SCRIPT_LOCATION; +import static org.openrewrite.gradle.util.GradleWrapper.*; import static org.openrewrite.properties.Assertions.properties; -import static org.openrewrite.test.SourceSpecs.dir; -import static org.openrewrite.test.SourceSpecs.other; -import static org.openrewrite.test.SourceSpecs.text; +import static org.openrewrite.test.SourceSpecs.*; @SuppressWarnings("UnusedProperty") class UpdateGradleWrapperTest implements RewriteTest { @@ -115,22 +110,22 @@ void updateWrapper() { rewriteRun( spec -> spec.allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) .afterRecipe(run -> { - var gradleSh = result(run, PlainText.class, "gradlew"); - assertThat(gradleSh.getSourcePath()).isEqualTo(WRAPPER_SCRIPT_LOCATION); - assertThat(gradleSh.getText()).isEqualTo(GRADLEW_TEXT); - assertThat(gradleSh.getFileAttributes()).isNotNull(); - assertThat(gradleSh.getFileAttributes().isReadable()).isTrue(); - assertThat(gradleSh.getFileAttributes().isExecutable()).isTrue(); + var gradleSh = result(run, PlainText.class, "gradlew"); + assertThat(gradleSh.getSourcePath()).isEqualTo(WRAPPER_SCRIPT_LOCATION); + assertThat(gradleSh.getText()).isEqualTo(GRADLEW_TEXT); + assertThat(gradleSh.getFileAttributes()).isNotNull(); + assertThat(gradleSh.getFileAttributes().isReadable()).isTrue(); + assertThat(gradleSh.getFileAttributes().isExecutable()).isTrue(); - var gradleBat = result(run, PlainText.class, "gradlew.bat"); - assertThat(gradleBat.getSourcePath()).isEqualTo(WRAPPER_BATCH_LOCATION); - assertThat(gradleBat.getText()).isEqualTo(GRADLEW_BAT_TEXT); + var gradleBat = result(run, PlainText.class, "gradlew.bat"); + assertThat(gradleBat.getSourcePath()).isEqualTo(WRAPPER_BATCH_LOCATION); + assertThat(gradleBat.getText()).isEqualTo(GRADLEW_BAT_TEXT); - var gradleWrapperJar = result(run, Remote.class, "gradle-wrapper.jar"); - assertThat(gradleWrapperJar.getSourcePath()).isEqualTo(WRAPPER_JAR_LOCATION); - assertThat(gradleWrapperJar.getUri()).isEqualTo(URI.create("https://services.gradle.org/distributions/gradle-7.4.2-bin.zip")); - assertThat(isValidWrapperJar(gradleWrapperJar)).as("Wrapper jar is not valid").isTrue(); - }), + var gradleWrapperJar = result(run, Remote.class, "gradle-wrapper.jar"); + assertThat(gradleWrapperJar.getSourcePath()).isEqualTo(WRAPPER_JAR_LOCATION); + assertThat(gradleWrapperJar.getUri()).isEqualTo(URI.create("https://services.gradle.org/distributions/gradle-7.4.2-bin.zip")); + assertThat(isValidWrapperJar(gradleWrapperJar)).as("Wrapper jar is not valid").isTrue(); + }), properties( """ distributionBase=GRADLE_USER_HOME @@ -165,17 +160,17 @@ void updateVersionAndDistribution() { rewriteRun( spec -> spec.allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) .afterRecipe(run -> { - var gradleSh = result(run, PlainText.class, "gradlew"); - assertThat(gradleSh.getText()).isNotBlank(); + var gradleSh = result(run, PlainText.class, "gradlew"); + assertThat(gradleSh.getText()).isNotBlank(); - var gradleBat = result(run, PlainText.class, "gradlew.bat"); - assertThat(gradleBat.getText()).isNotBlank(); + var gradleBat = result(run, PlainText.class, "gradlew.bat"); + assertThat(gradleBat.getText()).isNotBlank(); - var gradleWrapperJar = result(run, Remote.class, "gradle-wrapper.jar"); - assertThat(PathUtils.equalIgnoringSeparators(gradleWrapperJar.getSourcePath(), WRAPPER_JAR_LOCATION)).isTrue(); - assertThat(gradleWrapperJar.getUri()).isEqualTo(URI.create("https://services.gradle.org/distributions/gradle-7.4.2-bin.zip")); - assertThat(isValidWrapperJar(gradleWrapperJar)).as("Wrapper jar is not valid").isTrue(); - }), + var gradleWrapperJar = result(run, Remote.class, "gradle-wrapper.jar"); + assertThat(PathUtils.equalIgnoringSeparators(gradleWrapperJar.getSourcePath(), WRAPPER_JAR_LOCATION)).isTrue(); + assertThat(gradleWrapperJar.getUri()).isEqualTo(URI.create("https://services.gradle.org/distributions/gradle-7.4.2-bin.zip")); + assertThat(isValidWrapperJar(gradleWrapperJar)).as("Wrapper jar is not valid").isTrue(); + }), properties( """ distributionBase=GRADLE_USER_HOME @@ -471,19 +466,19 @@ void defaultsToLatestRelease() { void skipWorkIfUpdatedEarlier() { rewriteRun( spec -> spec.recipeFromYaml( - """ - type: specs.openrewrite.org/v1beta/recipe - name: org.openrewrite.gradle.MultipleWrapperUpdates - displayName: Multiple wrapper updates - description: Multiple wrapper updates. - recipeList: - - org.openrewrite.gradle.UpdateGradleWrapper: - version: 7.6.3 - addIfMissing: false - - org.openrewrite.gradle.UpdateGradleWrapper: - version: 6.9.4 - addIfMissing: false - """, + """ + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.gradle.MultipleWrapperUpdates + displayName: Multiple wrapper updates + description: Multiple wrapper updates. + recipeList: + - org.openrewrite.gradle.UpdateGradleWrapper: + version: 7.6.3 + addIfMissing: false + - org.openrewrite.gradle.UpdateGradleWrapper: + version: 6.9.4 + addIfMissing: false + """, "org.openrewrite.gradle.MultipleWrapperUpdates") .cycles(1) .expectedCyclesThatMakeChanges(1) @@ -571,6 +566,49 @@ void preferExistingDistributionSource() { ); } + @Test + void preferExistingDistributionSourceWhenServicesGradleOrgUnavailable() { + HttpSender unhelpfulSender = request -> { + if (request.getUrl().toString().contains("services.gradle.org")) { + throw new RuntimeException("I'm sorry Dave, I'm afraid I can't do that."); + } + if (request.getUrl().toString().contains("company.com/repo")) { + InputStream zipInputStream = UpdateGradleWrapperTest.class.getClassLoader().getResourceAsStream("gradle-8.10-bin.zip"); + return new HttpSender.Response(200, zipInputStream, () -> { + }); + } + return new HttpUrlConnectionSender().send(request); + }; + HttpSenderExecutionContextView ctx = HttpSenderExecutionContextView.view(new InMemoryExecutionContext()) + .setHttpSender(unhelpfulSender) + .setLargeFileHttpSender(unhelpfulSender); + rewriteRun( + spec -> spec.recipe(new UpdateGradleWrapper("8.10", "bin", false, null)) + .allSources(source -> source.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Gradle, "7.4"))) + .executionContext(ctx), + properties( + """ + distributionBase=GRADLE_USER_HOME + distributionPath=wrapper/dists + distributionUrl=https\\://company.com/repo/gradle-8.8-bin.zip + zipStoreBase=GRADLE_USER_HOME + zipStorePath=wrapper/dists + """, + spec -> spec.path("gradle/wrapper/gradle-wrapper.properties") + .after(after -> { + Matcher distUrlMatcher = Pattern.compile("distributionUrl=(.*/gradle-(.*)-bin.zip)").matcher(after); + assertThat(distUrlMatcher.find()).as(after).isTrue(); + assertThat(distUrlMatcher.group(1)).startsWith("https\\://company.com/repo"); + assertThat(distUrlMatcher.group(2)).isEqualTo("8.10"); + return after; + }) + ), + gradlew, + gradlewBat, + gradleWrapperJarQuark + ); + } + @Test void customDistributionUri() { rewriteRun( @@ -612,7 +650,7 @@ void customDistributionUri() { @Test void servicesGradleOrgUnavailable() { HttpSender unhelpfulSender = request -> { - if(request.getUrl().toString().contains("services.gradle.org")) { + if (request.getUrl().toString().contains("services.gradle.org")) { throw new RuntimeException("I'm sorry Dave, I'm afraid I can't do that."); } return new HttpUrlConnectionSender().send(request); diff --git a/rewrite-gradle/src/test/resources/gradle-8.10-bin.zip b/rewrite-gradle/src/test/resources/gradle-8.10-bin.zip new file mode 100644 index 0000000000000000000000000000000000000000..d478d2b51a9ea3753d918e26d0b45bb48a12df48 GIT binary patch literal 284691 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 `n;<{t1iv1J4KW-BEz$SDJn!-=SdHFc zFiI~ZgM(-?$j>sKi*CHf4>dw9au~Pd`3Z6SxsE7{an=9oWH9%UcZ)Df9&%vzqBXK1-^Nm8 zmZO;|@ozgeF=LEo#*tgAfTkpyCG3i{RP)~?MZ#ZEWZEsxdFUW`WX0W*SOb^beNHX& zQH%;>U%_|-5P6P^Z7+5&E}ut3NKy-1&i=8KxVK4}lhCLY1PuL)p(?>dIKCh?<25os zQiGO~onMNd;Wm@1RoNu#{fX5~ddBgEdz;UrJ>0fh zuj2`M^M8Icrt;N+bHb#?(6~3;Vds{bN@s_nwC7;0q$$?4oxu{Sq+FO5fY3t*sfni@oM+1wWmYk4BF%gdXW}Xd+GW02!McZ=vugiv(a^A!$vHubCHESdW zFUlf;J4Nuc4GFRc7A!SBImUMUWt+ahlu{0O62;%dhCogf@rt2;3|GCzZtXGZ75fYC z;q|ftu=OQ&%cJQQOUPK2Rp6p<|K+8MOT9pOyI1I!_S>$ogT9RR)P~4~f@7CV`NKN> z{mzw!QocFh?#%162au@CK&J&ThyF~GGyb#A+7{=S&y*BQ64aE4b8~a#tuKa{SM2pq zsn~wkRCPU;j%lL@|E(AMjx1&Sqih<$)X*Vp|E10hf_Fmo0Lhb3ZJ!u;8rzgTLM!ODM}gwJ=~n!2xiy1OlNR46i+K3VsGa;EUY;_~0vk5b{$MzFb$qF6&PlW#0_J93q za`aJwt?2F6OGuDyTM%2%x?kICtGj0=H+D^H$u;e`0bLRJf@s56b=VIQ?WN{5;&u|s+S_N_UC5gBT6~o^~dMn-;#k}z%nz_F$ zg%jh}BtnCb^4-V?en5pjentw!s6FJ@`tGy_aA$U)_hbm5XUrQ2AQ9pm4|T^Y2;3FJ zo;)q!SQMDz8b6JUnc!d3CQtBh8-xM(c#HxT@g?}SCeRI>O@Lq;xEIuo!u}*7Rw2LV zab598H7uXuHhf?jco!I;Ht{{-9w3B69unV}f!bS|`p&OuR#nQpYZ^L6t$O=^u($T~ z7@#%%z<)fK$KpjBkLMoNzSiX5j^5^o7h&$FChdiQrFO>cO=V+M8-EmXQ2PWSz&&FK)a=YwTnifrsb|0ZGtFzhV7HOxlEIxFS z2Yx7Qvu>7>rfcG5B8mBq+;YTQI-o_?yrLB)+oSMjFy7PIM5VR0_=Wh{ZgY}B>aNn_ zmH@Trx2pupvYGhKn_=8@wD}p-KdL(kQA)(0;V+6Pp3h}PO5N&2KFD=wWf-Y7BDk1b zy^UtIgmq^;b>LZ!qM_RHklDJd4YAay1I<2OdLx%*8I5v_`J;MXm_ilF>!HyG`D*_i zoGJC1kZ|woXF%}x$FE12rcicoJ1MOyRZ|#7;i$={olC{aQsU!Hur#@QegafNa%jS$ zWobvTR9BAMvE_g1@`O+@qLgUyy0@rc8?zUf3jM zYht4oR%W_Aq@)T7m;T6wRWN3~n;z6;Ytd23VPoDaS%$kJ|SnSoR)fs|T|EO;mHX$!%I)GZOB019%6fKU=i)B_5F z2xhTBM+LMf08!8qNbn~vfT{>g4SUb^>O7_>?`?lP-t;Ed{ONl$HJi;|Z~CV#j%2)+ zDUvXC=w?}sj2LuB8|HayZB4clZ`HJ`rhX?zw9B*yV)vB)U2VxVbCK<8`@N#50(JW~ zj%H#`{9t!upEFH1p#8>~z|@5F&fyEJtEB*bSrAQNn68k~BH)yU+cW&Fy8bTFV^fLq6Ow+xruEA-WA zPqa!Ck(#me*@QTV(}1zI>(P#+wOzIr?@&N&aZbdmWlmygU05I{B8RQ*pa6`|N)+GvalQ#CBWF9hbMR~DWotb(S+JDsM$>>yvJJ0~ zkxUCs7!8yziWT|jL?kG_m;-mtB`ie@jv&N&G5xH4?<{|+dD6mF7f_(A4*-b;<;cIR zSb~m#%d+d$oO)G-LZZKA$SLhv)XppPU92PQnWV|vBO8)a;!CJbvz~6HOl*ZN(?p-7 zq~z~z%mxFnz zWlux;#wxp!nl@=S)CdlwyjZj0nPx7~ONn{e$dN^|tETCzuAcZeW!{}EFI}VlmyJnv zHtV%|Yb}^Y9jd`a_Xd8-18hZpQSs4)s09%ZU`;kxG0J($vIwT62&{8E<)2Ml6r2F^0)ENK}p-gj{6HJ;>9{c#xZ z1z(0grab#?F%UTJW_NRZkQ6E5q=5YPbD{ICAX=WjdVxOp5hPb=4qt@udEGs($|8iM zn=4A*#(ad0Jz_`c8XhhJpWetbmv&jO{8I8HS843*rSeLmSbL}wavdVwa-fD^l{W_I zdf^E>l);;B-nY>*S+v=^@a5f!YK&=jy~=FxM%&~Z;iZ>7QIAN9G?=_`f+^6L@qRjA zt@GhN^qSq)pOLKX2wQTzfIDvWo8@hMIHNT_^P&)mrd`c z(X`%$p>YdTptjc{xr8YZ$^ge0(^2U*O{csYFfz#AC9bYUqEG56`Dr*syJ>*E$v{>v z;fiDJeiIG9F^SKE@olqS{Z?&>7{Y{m-Sq8$egM*wKZ8f~EtW>eOVLSr&C7K1wEO~I zPnP&BhbwqwGX`>0ZW&93|K50cd;vOqj?jvPCxh1%bwZGXO8l>9NDu2hyNKS!48mP? zrCGi1a|->Unl>`Ff1ON*U!w`;ml_?fZZb30N0w=gY=QI8)=~b7hB1icb z)fB{RZBa`+>yz^-6@g%QI>gCqO_MSqGL|fn8e#P64Cf>k_%PeB)_#!1f~HxOD6bW5 zHN0xwT4LRbEGZa|BhGy$fm7flQ!(21%l$U7AfH8IKDl5jYX+LAHW*q0LYE=8P!HURyc z6i}DgUwLUsysTbtz7nKPPR_EWFB_dJspI#p>;HT@<}A)89%~a7nytzc1wDd2_(1FA z9`l_c<*@G}c|=kyP47~=v>OlRcc4ygo0gM&l9hg0+J<{SH9&=S7NXbCge}>XEPKm^ zQdD$dpLV|7Oo;X7O4EA{_%VdBrK9dbmQ~||X2|%`+x=BJ=9REWUK44;?a4GnN+(-U zZQf)*zu>YE|2m!3?1^Vy%5^m3x*H949^1=>#C#o@fNHB;od&+xS<&n4IH;3=VFs7g zR7T4B?9_gPDK8(D_r(NxXvL_IRSh>Jl<0}Hq>@?|I&Z3~!$(d3rI!+K2;MrgmXNRa z5YyB%jImLaI_rbrD5=@A3AFiMd-bG=Qzu=R={R8GEKG0E!D;=@Ix&S|;o~TsbIKJR z*51!t7Jx9k(QMN%ERmkfHmty4pJVh+J34=UTRkL}xB66N?4IDXJVCP}88!E9>MfV$ zVUqUc%73l8tSIe=a#Pn)uC5pS(AvUz`3l;#yTEneJvhfQcVi~x|Jt-rfNbh1SirQn ziNs+_jwH7k=pvOVYn$kkSIVK0>AD7cGFM5g`&@-GcbM5e^<0lb!sPsBj5M(i_!*z;?sADud1>AAC%6Wp+BqnJLPq(27E< zlq1)ft_|MsA3Dq}%I2SjhNaS!NM4f;_Re48vLVOF5UoV_>oCxJ8$>RLqRE}@_`VPp zK^osPCwwM8%A-0qNZa&4e~bv|m9)i+D3z0VLIf%_Ow5c}GWW{unJ*eb>!G4ZRz0E~ zVvCtuu=z@@e7altK5nSBEF`mc{EhXsd^G21mWzd>eAH0qNbqDETl#!RGU*-rD;wY@ zzPrc}@dkRiE+*qDDrG_z%8meGbOz2yKq;S)ReKZuOKNDuNB~$Ma`$Y3B{q?;Q_~jA zjer&xErzCqzhiDCiU!9fiU3oZoDaN2mAlkYGxJ@5(-l&>p@^HeAcd5re1YPoaukFZ`)Zf_7NJE2ck3dkGJedv^% zwYa>_?=Ws-|MAk_BsNsgOS(r~PaerG1g{0WK82q>viQfxA4Rx7D-iru_WfTGJpcvv#KihEJ2d6cX{P zb}7)hjY~jZN?r?egoG(!#SvSTrOH%@F*gnt(!!yv0&Dr}BnGw6ViZQ@|8R@7_d${t z;d7N)oWgXTk(@f3jVZXC;~^Nx2tieI^CssD>cPW%k}PVNn7B}QSi)$)e~)8i`{g^j z-X|IXJM5>qe6zb984Pd>q)Ny|BkyC)bPtIL>DRcA zvjGfc3v-2m7DMeK@>Lx4BfB3lhSeSi#)dGiNKC4!-xt<2l%0{kn(wXY+w`Wwj_o+A+ zVk#`@n>%HS$BHwd1j61j_;R4f_lQU5sa>*J8kA;tDhxR!@e0Z_A2=|#n<5^m7Yu3VwV9yc zQ_D+X`&3SolE63eVtRtg3!sAeVmEux?Z;<8`^3pQ+4}STviTOZTbW~RCXqK#cYNj( zROD>y1;BS4s%C9wMbL$Wq=@{tmG1Zs8+LH1zwc8%Vvv1U-SYb_%|AQ);4CY495ES( zaPGS}n|ChgJ3{nlG46vy_>wrR<8QYZ-+abpBqQ>Eok?!IP=_O^$RB2^pO}jetIy-u z@M8QAv?X7H6Yq>}dl3ovJJSu753R3cv#>db{7)Zb?jG`?v<(i@f92kvuE$k}kP(lP z1Z9;To&6~IfO;5*tNYD5A^&RRK=_!xohO9s@C!SdKEB%~j5xiP@-g}w10TqrcdpQU zus`Vi;u6P4=R5L2SjRTBpHDU}K8nAIQzRGYuhaVM$2|D9!~+iE%(6AXrH^A_)pzEE99dNKbEwB$oEfyQB6S5;IVA4H881m1anEhPXJ;Tp;TE*6H^6;y0AT^~0F!nqfH^lQDu{M3bzxXvX7;qH}>7;u^2*yzIIqBR`&p!FS31c&?$o3_Eu5QcU7i_ab!_$i>Hs>z6dI7`{X+Bb(+DZ9|LHXe?&?^KcvM@p( z3OW$Qn7U=gNv_yTw^#l8CF;n>VaG>L6GT)aW*8~+-Of!*C);M2j6 z)|~cidVCkh6DBO}u-f!yawm)$ghPr8Cbu#q@-pA|T-hZ;jc7=zkgvyaZxbH@I1UAF zsA3f68(rFLBjGxHn#@d+XeEVdB^Spma&6!-i*)}JS@hF8K8}TV?b-orL)674`Guy_ zvI5t%)?5MUx_;6vJEMN5ld#kT^0rzMF__LEtU8KdwJc1(HSVLyT4x^70=epkZ;~lTRlHzpt zU%ZR18C)^1-(j(igfX0rBO~@>Wr@-uNk?=Vu$$+!dcTP>XGJ_iw6YJ-td9uQPlji5 zsk{n*vq-oeRU#G^oo*sC$ALCCGt|sPo_-{rC^jusye5|2sH(){`cc9|Y4WJTVoMdZ z?h!8)QYv5hUj-&dnBx@yB z$Ip8w&GV02w0>&?sN?l-=L$uC=n_Y9S4(b)^Q&ujneFlA_p$bG53K4bmCQ-T*WG8e zXSDY7+XJWUCky8{Y__=7kw*+8X1=}S!GS^)b4b`{IyFOY(qe&%=jJJ4Gx4N~zsV0y z$%ReViYjL9NN{zj5Vx3yo>Dk>yi^4JigGFA7IkFyjy1&gNv=I%nTs}B0d>;Zs3Y~u z;5O~Ue|v11iw?Q(CR>#EO4Uw`ZO)B8q>u=;(zxgMMPM>qZLF8|3YY zndI4hH>JW>_ua!k$GOz9!99Z*IzOdwIDi@1b3uc}#aTG}M?L!Z<6LK2rS+dc@gyWB zU9f>gvOceO`#ipFL|O}jXjZ>I;E99OwEV1B%;EZ!ewUGzDDoV3NqYyIc^YdPL6WR0 z$%+adtOSY+Zr3X9)8n!bqRbxyrdeL_QK>bt}1a~c9U?qXRv3Z zUswACX=3u*n|bWCLvZ4=Sr^vVL((7m^Qfq7%yHW_$aeahJZJpZE0ZMIS#H;wimLLt zFFrMSvkv^=Pc#S8_*b3vDT-fH*{4>mn2f z5nsbSO%UygoTz7-0khL#voWaskbDpe;FK01eiA*yxPJD}1zZoaLZFv^Vb~4Pp8R9g z)<^G-I-`EbgpLD)Yxu*A2(*^H6)nNmRN7&%BDA+~)y-|4UEVgHojkjkJu4i?#z!>5 zTJR_obJ(NX!r8fKC5g@VS&qC?VCivKUk$9`9d*tXgcu|Y+}i1^bOYcoS5xrQ%(rLJ z(P%zlJhs`S7P}^U-AFPhgsuuZebPnJ+MV<{*PNpT93XBKBwZEuHXlBELP3G6}5auXXNAbyLPDzmYeYMJ$G*jl$_oB{$6vviHhcUuWRhSP#mhZy8AOek7`bBkM`MM$9&>u(E1nlY=xI`bqUWi#Pj(IRHfO zWpypCT2L`7>Y;~}MbfZB+v!t=))YfwU5ZmWk00smzMqMf|AesT5iol_`U=l-^=S*8 zC^u^KyX4xrWnD{|l~7fZZ7)HzOUPB$xoU$lyX=@7c)NsA_j{oCfyiH?`ku&-KOI2t{c9PyMHB54VjF@QRuO0gDW^mD4`$i5PCEeN|i!v5E``YP@y;Axv;kjU| z)BCYaw#1i8Mqgdw1W<@2+Jb*OC@JDff&I197ug@76c5gLYL9GJVr0$-4UqB*(7~vR<2dKXc(b3ymctm518CZ!kfr-u|E|o+?FwZV3ea>E47w2Ac7uXB30J zC@zDaPStSB+JNq0Wcz@O;*Jf21-+*%XKpvUb4MT>#hxr4LFabUVp{!Fp0nk~b-1+V z2>b~Kzt=H~BXIB>2LnjxAwr2dis#m?Z8Q`(Hvf)r*c76Y3}XrkCp|Bu+SL+yA(^GU zkf>WSt9v3)D~9O?No7{gJ7yNn6%bcoDXV=tB?eXrZ9z|YF(YhFP07rj`zNFM_x|dW z08xbH8P5JqBDQX&SEjfj5@KC_#dX?9JsB!aON^iK$p^<;za;ao0XcJbm+k~bGoBF{ z_H|uu%-XqZF@`E1&*b@+Z7r*hv17Y4$JB_j5&@8fx)FPmqZd2Q%`Cv&wtiUrQodg| zyuf7X=Q~={g40+ppZ)D@DcFeGq7llwr}Z|T@w@Ts-Y;6vkL0pVDky!oR_I-`KA4{X zQWxhW0h1R0pVWW;FMR(QFBHkJW-bpq%xU-t1N)M77-5;#!* zS!PSWlGXj;;DP#x)40appK=TUfk9A>8}^Q93Es`{wmZ&Y1`yuBN)Hqx$0w}Mz+((W5I(<5^)@na#cuO=9cErvKhS?z^(7qGsZ5ye zL_ShJ;>UVNm7vnz>fF?l1-n}e-7VD~)+A@I<@mD}L`Hij2?@3lKhPjE_6S7EW4Vmeb#;YV_ctGY~ z>)B}d@l_pf#x3}@y+Upes>>A@R(^Dr2gUbaW__vWD6uA~KqP6t8nWEF;|&{kmFh_w zS{O23T?J?D@?IYpD9pYxbKM+Z7a99kvHqSgbe?*LhG`3_6T8Y&L92?e$(Nj@OXhwb z%byb=+9|82+qZ-mRYdeT&DV1Lj7;}-=^3yxu<-N~@5Sa?r{!L|=8l(G^KW%jRhmR} zKJdfyj_ar}Y0QCJujEg$r(BBzg9PfXfWnA=ecj{-gwGH(KepP%lK*;JB6UCB-L!XH z-DN1q6z#+_u3wnGT}h~YBQp4^*yP3`vT3nKLVkhq)x`p!L`QB!7N0vt-Z0S?RVN9Y zzmFN!BSo?sOdJm%JCBwYcSkPjC07=oX(E4RSZlg#i9HKB#mn49UFwq}TwefyPAtvmWsC`U$*$Fa8n%%C5gDZy+?fM!J zo3$UDQ1-*jXxE^x&HHjfCB~*y`;Uby^sOz+qrMozlwK`GA1rM)z1xiH1cEtzhsx}0 zZgu9jWRqtBbizi!eF4EWJimA)NF$#(dY9DGcW^uKtu)2zG}Wtn_(N<`9SWNGlfm; zh-P~S+EH3+dY%P~QNp2Urna3$w4zZ4Tq)yZxPJ#nwf%ISr^btFM8O9!*v2MMc2bzz z>?<)H4e=!6f&Kg@i^PM+zl}bHy^ir13nheT$Ya?_!71A)`&Kc%tG_-P!~uJjtBsVg zk2Kg^ZNP!bn0lUbXhGzs7j{gNGS{ciY)r;3I>^Zd)?HK7)tw$Gt5%3{mFqJUmAFy( z7MDFf7HJx4zB_ykskZf%6x}^}Ty1xASv~RI{!=Xx9!`Ep#%%p;YMjU?!yqvrk@3nn zg9obt+s@UWk{OI&qH+a`Ne zo#}>_1BJ?dEFF}jE6*(KWl8k5O>No~6@&wuu4B zLM|^Q)iR_AoQMPK0vL;MRlC-IUryCBU9wJcerc6=8>NA z-@KH68I3=+ujuz5Ap`%_2dDUN?HTAr|9r+)7DtDFQ6yd6Qnc#vySJ!rW|WT}+)_Xw57=TwI+@o&S$jk*fi&fNa|iM~W|jE`%s(DTn|h zK~GfBf&dhvAYxhgrWY)gOeG3g@Jt3FfsBwKcP??aD*@X}U35OLe*3d(?{d4-{4{sn z_r-ITv&-Ckc3ligOaul9P*MT_AOLt@jtKPI=4=jKS5gcRXhi{y1lZySpcmJ%OXcoX z=G`W+4}1uPrC>^05(l930kHR_h6{#>Q4}Ir_xc8>VgVf+b&&SvO{lxT=Yfx(k{@8h zyF%o6@qxRCCFZcVW3KRpV5~#$8rB59bDd8o80YRk*w1^YI#oYxcv-3z(l>J6$O1tTxoZBQ*C^Bd3I^{ zn*JBiDIA6S+tD@&UxD0beKp>&)!v{Ee7e0m!hdfmHiDq~ETWCpiwp=|$kh%VY3qBY zq;J*Qwh6E}a=MuzWl~dKh3=molECd23|C38@+7QMtZpnjZ)feX;XIn(@>EwMaHqxf0;RgQ)axgY{8$dT3@k`@V|$nJ><=blYhzGM%EOlSw72N2%> zf}ks-6yWLta{E0&u84E@lDG~Q1Sh9lw+K`4vX7dLOs~raNH$&}w1H7dv_RG)czwDI zBCE_PS!0*sP4}r$nBwvfF5P-=Z^l)&`T(os^E1jXIy%OGP-%(S<6~?0^j%1@`$PWK z!Gp08X2AcWSAk@XtGzX6JTJmqw^{0>OnOd# zFv>~ljNXRZ)N2smQ$r=7ivH6a<6RsIdbiFoWp(9qbyl4oHU+WbMVC}gM7+$DVuEH< zo5VlAFZFz(cdLFM)$^1D=BGdtZ;UfxwpT^njhmV*p8JlT1B(rG#0y|vAVe^HZV$9c6nl&-C<41Y0#Jm z{3x*13Ea-$=guk%9bWDzUC%vHSJCIu(wBmrf!wZDn9HW_q?u_Xqq_**w{%6p)@5R7 z@r>7t@sXtv^6O%MUN)l*x!Qf@Zf4B+{MdEpq%#)}%bd~-M2uX>Y61s@X4Yx<+9$5+ zCeaOtzS{m_G)?0b7su1kc4))$$^e@SPgTmw8WM43bOYQWR6oraE6w@U3 z9PVCUDglx;Ush>Z9qAB~VM>1SoYE|Yz(FDmFNra2j9j(tQnKs>8FGAjJbm$2GnKV zB;b^T>Vo+hDhbyZ9W&w4FSWfw1L21tB=q-hE@;HjaXT^oJLt_(I3edU{plB{Cb9ez z8o1Xx22mDzuH`qk-J+j$I5HlQCY98kKC1+>Lv9uSmvLq!7Fl3hbpEUOZb442o^EoVP?U(4Ta_elYZ)Nvrcv@5uv@0%iZ`>zZti<=b&8IVXA1LN4q7k`uckbg$?H>C)!r-%MNogpUnr={*ZsZiL~JYMj}Y<*aNeSA4GT&{sF#+d zzKb;B=$h5)zieZ%qnBLXbnBOTu#Jnp1WCsih&UR-MU%w$!6Z+L&pqdN>Ym9o$u>R{ zI*Zm7N-S>y|G4ySmO?ch;og|Xi`YjASoQhsJrxvBJl-LD4>IVao1hBjtLOLlWBR=* zHLnc+e8kp$j7@Wk>?F%#B~NaZCu7wTLf%h_(z>j-)bNF0!+F z;SO*ub*__6YFAyG-W8roqwOk^beoB%pEmpom8h=D583E~u1_=kmq0 z(rr>-tHtF-SCB%iMStR5Kcd&^MfuiF%L&2D!}SF*>cryTHtl%KIu0)EGhf)0xd^?6 zMK$EQV~ZC!$QP5rlX;yn;C5@1H@V;B@>#SU3gqB%i8caQ&+sSLnYAzZeSrk`SG9MK zSdCW8POc>tck6$Gh+M*^93c3}gQdWXJToWokS}Sy4kq;YC1IA`6 zDLWa$OqTdw^|;z)jlwAI!o4sbvH8fT*kIyvgP-l$=<{G6?W zua(YJn5Rn<1H7)awfn~*N716U{>dfqRdLwpVlJ8!G8JjuSp?(mwMN&H$xePLwWk$L~)o2~gaWivt0d(XS z4!L=BuKKjfEIzLt_mrEMMzqhr_5+NF_KqxkD_u-0P025|C&Yk@&7$~xI5rbPMyH55 z_(g*V(p@k-mH=e}>`$FbBaDD6jm{lNf6kq~$T9)$cCl$zyXcyh!+Y~Q++^4#KW7uw zS#)C&G=-hu81Jinl2a^@JMEd$V&0ZRC3j|F3=e55}l)0UsJ_0>^}Rg`qZ5t&D{hVU^J#)VGTKm4p?X}RFGykQMvzG7Y7Q{ z=a&lrF3?5`c0dJp!(K6l>^qc`cpU8#QM0}uY7|U|43&Jeu#K%`-T;w1=4g@KZ*8&3 z#&OnVd3(jOMwD@UshOgE4D9yzix2-W;%Vu0%G&y-j+>|@;%1$IT9?MCm){~n+OVmz-0+cDqoK4Znz1}TI_mWGZfGCNrNdY*TeUZGP~cT>V13QoVI z>hshoXhu(rYr#9Q9~XQlw`&mtvz(U$h-wbOb}6>v$SC1?&zGTuI^?Me8+-FW49+zu-spZ2$tE#|1$0w&%-fVWQ8Z|NGxyU|#J>(cm zU>_hz3WOU9_}ZO}CY=R2t0cFiQZH6U>diW0D`%}*4=90+xoILnt*4gt^icnsH_9u= zr}D&l!1I%}l&&smQgn@38zI3)j3Nv0pbzruHI2=o#F0hmz+ z05CG==1;>lI{Wu-TuJEvgVs^NW79|NRkxR-9)~dclY6aByAFLTP>2W`#C?pb7c*AxiMHjCXUtU~ZCCQzF2F%3K@D(p$1 z6@7&Cam3oFneR+JX#=?a&pDRounH=RLLN8Ja3K(=`)Ca(lnhQJE**$m`|&N6mvaRH zz8)W9{`vzfMu@b6v3gGDH)XLJIO8f~!2l*JN2x7E! zv_)ot+9CorY%C#f@lzB{$rf4HNm%~x$=f{KPZP~eZ{2lUwTF5<`GcPYA~l49ya&J8 z?g1G&bwhG3u3JHu=zl1Chc02busOGF+qP}nwr$(CZQHhO+qT_LJMVWUXCw>n59hve0^S5RLMmVq&P3btz?} zQz&%GsXf~F{yI*DBAN&T@N?IQgeP7?S^dA-XqFPg6gFjF}i-mC!ZneKM3CDZ711^n~1hbP&lIUZ@OYua|wk zQztA74vmtdl?xMrm;VuqpXV7v^M1>{rFeBDT1)_f$cr`VseLh^&5!G~KmP;&vfrA@ z?|2^g)31=>hpxd9J`nj|G2eHb;4g6Tqj>L;yT}!Og!La9m!G)Vci@c3gLLYNIXQnc z^`Eb9R=?X5cjc+OCeu$sDa=Rx*UP+~-`)M6?D7x0-f#Wx@3{rPd-8pe;xE5UVwdM^8alOGradS2a#Z??}YQPE9T~&k#!}EziZsu2D72GTzNV zev?)#;g81R)c8@txxPPwYpX+3bDQ%I9+fkcqc{!<=%5S?Q21e%8Jn1uxa=Q;A!`$x zn|tnI!1H)su1O9iW~QE!GypiUeGs7w)FD)%6VDZ5xw!;vSl-0z}b>PYZ5y)4H zk%~bMlWBe_z%=6?Iv|jW zfc`e}>1G9&sQ}A;2|^jzZ;Q3CgL}sk@##=j&z6;)G*9N{VZz;iAn0TqZSfkdZMgsL zX>cFE0?OvJTFC9)42mQ;)uL?U{*0@i2}Cx8R;PL>?L-~wVN8Sj;&+lYf#0(s@YLbu zqm@$*A$qJBEAG+C`c(?l2$_Sy>vBYM@160LK+}#JS*_o|I(===bT5wG-)QZWJ~Kh&6kN zsr(Bw>s?|rNKJ=-TjFKg*?)^y|EaBqk`~ti_lwW0346A*?6a5pFZAwJk82tt%$a0XX2RyMWe;(A(oqdYqriMR7FrSKW9?$z#= z1fD03%}-{3L}-!!K-7rFMvsH2jLd}7>C9Pq#|7t=wy40CncEPOiB?*hBnX3>_7Bed z^w0I&+t`$!i~UcPE0l^pwusIQZ|69#@-iHTqmsf_XtS+7j!fhukU;Tt?I*WpVr2ax zA03|020qBMKV!zjw!5IWo1arV;pDNsHG%El?WeznvYfG}o0p-dp`X!gq&Z0!94w76 z0XI~SN>4u8H)v~l=JSuG6OF?{bCGNaQ{~sx&(PUhf|eVdIEp5()y>RzmM`B=+nB*F zJPaZwppsb^aTz6{{4a{Qod!w}xj2(>5SD%UxcJTr*sTr49AV9PQHi&?Hnl7a4f zi)%7t+OdKiStII-pqcxt@X%XjxMWCnyiB37)<)(Y7mtTj{}D>+7A*%PD38Z+1{>WW z<(#*AaGd13azIN?0izaFqF8cATcL3AF)^PHkivStr4qfUqJ-i3ewMTCTh^3_X#AJ7 zNALKx5UL%jpStepcp^+{&kroC)QdIpT$QA3sM;8+G$}In)9=tSNrcj$uN%%wuBqdZ zDW2hd1tDr4IccjdGg%WzFxC-yl2|I4D@$(b(<%nJ*BO7$BDag0q67#2%F(VVCDY@sooZ_Uhmt3VM@rx0PqW%&CCVt=|V8DS6h z6^3wJjf#>G(%GiBMDOo#?4|>>LX(!#N^%@Y4vQ6B?ovqaAfIul!e)frb~3`jf@r9! zLm4g*qb)F+-JGT{8rxH38i`1~4HO=}IGh;WiqZd?V~|;0sv*+taifyiWou0=JK73P$QP|<@~K@>1Am!G=EXnXVrS+UT0nU9k4^Y)86oTu#-*Xc&GWhvHP zVg~6DGiV5Mh6oM@c{#ulMSEsOo}+m2MdQ<{;oasZ52A0`3ug#?yHt)@oUEW#MXP={ z&r~gW&J^a%*$QDUa(9O=qve5hsfAIWR#IT%_kn|dZF`}kEdf)6dV}EDj_pdz>I2!W z=F;Fxq>vbCAJmfZ?7N$u;(z{pnXelc*PT@`Xa3e8PdXH$LW`5@4qi-pm!I6w!KHkzw zKVj>(zVS%@8sW0ppr*>D>v2&?A<%>aN|d^#qxvX3ZDcMacD~J9>kq~q6*vX@6O){Y zG|%Bg{vz1C*j@7>!k#p4Cg^|(K>w7(MaOaHOoz)q8gTV4#TSSEb~4I13wKD94O*lz zE|u(&QptoX$XE=rcz<(ReE9!NY^~;+b2Z7@Bi3yvoZb0MICQHCXH#<;nQg&0q6OA{ zurUp$$M=$na_53;?iF+mT+ z3>Zc03DvJfYXN5)H3LIB2s|TBV_mNvK~P zHaQQ-Ie^0z4?pkRNWLsRbK5xzVpS8+B3vq0_-~q0Kht9X`B~2wjp-yiSz|_&Nv7kR zqZ}0gF=w(15gf9Zq|2cz-W6SBgvUd;!w_Di^X6inTty`=N(KTBP;K#b6B>q}uYgf> zHwJo;qUZ!}QctZopL3{HEinMQP?x~e%8Zomyx*EA$if*N2cquuvhA4HnDGoN%;#ku z9~DAfY!X+o%ND61b)hLlx})cc)g(r#XH>76gwyD;I}M^VKYAise9W^NKc^k`of<*M`QanmI6v;L@?ftWrdtRVTu1-M6c$n2w9(QN)zhE(*V^ z(}`fxbhP-y7DkN=(8f5Q4CBxtSN;s6(S>SLhA9Gy4pYkA|7I2~embsBF^)@hmQIVL z!#|kIIiGeJo`(E75hH)}jskWdR2?~>3Vs^Hp}_JqFe$|?>w0RX@;kW#oIp=8@TZWN z(qeUY(Ml_5)K@}Vcy4)$E~taMq>uK}K8+D;)$!qMu}`|CSZhQd4I@CMV1NztPFMN5 zFr>UGU(^E+K1K`;W&;~JZ5P+^OQlm_*&lSOF!(?8cKX-2NS}8jodUMMFq1%8rVBJu zY>&Cs61r0Do=;PBn)uk3g`y}66Dsh*oC?q#2bE1kVqT`i?BKvjwc{>|;6OnnV^BmP z^resL#ume(%8!bqOi)EHa?#+$>eBy0@@&Kh10*QPN?~Cp8D~lX1lke}ru0&coW@Sb zo8ltkMfY%&a*aKzM~S7Q$9qLYTdG;*{CeE=|jiox?Wb$w_a=$ z)!0gpF3$XHX|~li*}J-@QbFin1CnI$ZnuN+KyAjAJE-$8@DjPdiu4B5*hpWz(G%4w zUgqtOqpstsz|gR)IW35Cm4W28coy8ZOc>FK9~02GmbV_ud$C#9!tB^Z@`<*c;LLT@ zYgEKfU41n#o2@)fC_g#3MijB6+d1oAR<}{ssfawj^c3FIslXS&dE?2k)Ka557kq@{ zE)QPA`HcDrTE_-PZsrEXi~oa>gzEB!`&K#T8^1-9NYR!Rbs zek%8i>U&!#)gBJd3f#6kUm9;26HppGzPQ!>LW+yYz?M!0Pud!|^dDkvp?(9Sslb#< zN@xqhF(rZy*uNW%Z#LCoM+G*RbW2sNaxch=l3H*CXm!t-lmg8I%oI>$2sPA8kmghX z4q2Bc^@1E+XkTuz_Ox2#m2ayifmJv(cYETn4p5ZFmV*1a&}5<10!U`^-MH|5lSKI> zPQk_xILTD$^(=I$>(fMoFOmN9VmG+PR;E%IeuiJPp&aRQky}SxG#`*MwIZ0uLo_Td ztx)K4M!~);>`}jU?AaUTG@X@Kn*~wHH2`|+>N82z>dq|4gyIVqyG(* zLSokeIuL#-R3DP9B)oq9`u@4NJZHi5d*|)JE~K4=Z9DL!cSo0$)j1v%f?RGyM)08>%+iO|t|I40k#fZlJdUez@^4*R~j?F#CLMW?@Q*0D!*6_oQ; ziBmceFkv=Xh_{!?RpBgIpRo>dtf9r6)1T|#*zhGvgkK+Xde~{neQk!T3X-Zzq6+D} zg~e2A&=|}d(y(4aQ?nEmx3b6{ZClLJK&0u>bQ58wx?fzW4Ze>+0;AWN*`G7cV5@tB z+8&BrY(^3rlP{A$P5{#2yG?_{rcyrU{E)c{g+flJj`35{4w`=}SaWI52U=u`9{a>K zMTjj2YGi@os#VN|XbLnIw7?#Rf#zAPs3*1s^3;Ab^HcRbLQh-gBHkG023&=xjPw!u zAB&)P>-vuzUv;S89gaT%c}rZp!0!o|1@WFjqz8N84QKJ9c+u6j1V2J( zqR)=iI@C5;ctHUZC@Dla7a>VCcuWKk>*O5m<*QF6VQJ4oc@XE0 z0c9^rKX)}{>E!Z9f*zmobX|i77+=W{c{813VM-y;6z|d@@il%Nsu{<0L;*yTwQ_^6po9XTo7<1RT+d^rZxxo}|ERaXD zm$T6ZU_j+OtfH@J94G;awQETg!8329-G!|#aR($({x0Id#$2HucRh4wZ$jm7^o^TxDHu%En9_uHkFdvqVXSHx$tg4S*C@kUSzB~PoE@or90moD;p`7fR<@@ zw3`c9g6#sqzc`^Phi%=Wv-0A*`~$E}R=dIu#ZzfGWA@#7 zqlnl^S_ld5gYZxiI=I18zYYKZ)KBjGj``St+v9}ns&tSH`1ptXEscJW-a$)JOH0zC zj>3T-XXk!ft9H?D6v2LD=blSzf6-1B!TxY}(^Y4)B<|axUM(P)901)U&flS4Dki%*N2N(klm zDH$k?E{J(hA!##VErLON26~b;%r-hwiSJyG=jQJ6=3-mde>ikMyXH5HulpBvuaD4M zOp=+2L_2j~*Q-$Xmbe22Gdk8nf~yY4I$hqs5W0jeUnD-xq!JOmxlM2R(_36V6~**+>i^ z%GvblrLQu%Fso5-|uv545vVNOF=?$~}PMUM??+h8V8oD$_ow z<(iEiumZlMDTXD!TK0ie@JrBDNpwQ}uosb>ySdsG84&fETwv4xs0Z_y2{KlB=V{H3 zW=`I-rN?_Fv{Z8ZNfdxPC3qw z%P9QgEd2qUGd(OZQLor~-?r?vcA7D8O9;jJV1rNNgQ=D*%x|=sU4=*`#GCTJqma;t zLe_+3Nx@KHauAuqhSi_(<4|)Pa+7Z24nEfrT=}usuR}XCCgaLzE;9|lgIkR*e5jZ3uKlO z6hEIWF&y&5ZYu*SonPl%4^fO#@-iNG!)6TR7LC8S6zxYElvPZX!d4mhWaHm zv`3grF*9T)y&dE}!#+inyCl!Mm?hMT$eiXA<2(6t>5xt|KV>XKqD+h=45N z$H}q!(ayCEQV|bJamgboM&3+4mz}g&N;(<-?5^jmaTAKWk|%_6X@<}4uZYPjEWb+xT z|4osyeLjrgVYl+HHH`Yw?)cCuwHQYkheJb>6ss)YRmH>AVReQh)WM%ohAJ+CSK{`;<&A9&Pg--H=#Wd?U1zv}kiiX)fIbQOT>1-( zQ-~A-eyptl9|m@SZhFe{tL87Ai;Pzi< z8L}L}JH7jT?h+ITe+PMocdISgixSmgH^BX(Z=dqe?RGwo&-~4a- zO_kM*hAOJTAFkoQ_AmHIY_y_-z_L3PJ+?gCdc_&noYl{NQ0{A}VNJ<3h zckYxHdVd2LzQVQ=bb2wf1i>wm-XPOf|D zp_?f~{TcC>1!hjg%kDZ4Kj+w~e^ui$B$HYpkS4T##gfQd1!Bt=s9yxX1mw>fz?K#4 z%(wiuFK#Wp|-9)p(e z%DqxK`Fvfp@hzKPcB$SCKZ*Bn%9p;K{fMWN&6>BCCP2MB3e^ij#w?(;tfcgRKwhq} zm{T3A_lg#z*KYvlo+76fPh546uU+2yAF-P^MZ-{yQ`y{>_wOb^?1GfuNr4lz+6%R=5pBAi>UP-^qIe}Q6 zO-Kix9`MB<#+Jhq6$^U*HFdskC}-=sXu?Q5S83gOZ6Yu}<*6U?%6by=7Ldud8q;6a z<*7(#lU60ZF%#ediAJSP+$pooB6>_s5_&Sl3!APb3*b9UJ~;Gf_9lBJ#u0RV;jy_A z!i|wzq4*~g)#Wpl_j#(`fm^-TB(M)KDnp1VWm3w24Ihv%Y*f9Le^Dr#aq-K#ndw$< zf%yhQ1;#T)O?7W%tnV=^19^xcD>B_kAq{o#vbQx+K6rO@W_SZW11d|mGh{;^+ z3E#zI4*aY^&Mc+kO2D&P+{aD1QQatnI_DWHUu!-`CS^Vg>Frck9P6mO{wF)rjBiC( zQ|LsCl4mY5;Ihftbo=b7%6F|gADQa#S%Oz>53vMWKQC><$R*7qzRtsKLC`hi46_@C zJc7L;5KEUVKqa7)=<1;|xN%ybiW#1b!?KCwyRfTU6Z36_PJ}L5VdL8LFoZKnL#pwq z4;4Q6Uw4IYGV^2czOZ?PYBbeTD(z#q|cmW)~=FNeA>eWB!^65uyJ7BNEnd0aT6;8}$ zX~9xasCFo>jBn|VDUHb$ z3X7Pi%`v%Rq#K^q0H~jK9!AeHf|{rix8MXWs#R$>&}x|ITHk+X#FHV60SX68=Q2bS~${T|b`4|U?j1HWQJV?jh*_>T~nnc1CK zT@xL@X|C?=jP6cM%Dni8`0YPkvDV}~jyV3P?PwTn7(*Nd4~PyZR#AE=R;?izV#zE# zbjV)1KU7Qbr7MU$mlroI!QZ*_1nm6RMQKSJxO=aR4t{o={<&RE*-z57Xk&6Xv6c{@ zruHGD+1B%5{$YFK>E3gzj~}q{8h%MT+Q(uinADEPWc=a)kYZGLM)xcnxf7#(;GkOb z@x+H%HEPxPj0&JzK^BC|kXXaR;%)4SLwykQ&xv1l%SKb1al%wL*`S;VG?m4SDN@OO z&`p}c+Orw-oofbgN=Q|t)Z1D3@W9m!J83hQV#vfB+eO&AR6U$B2Z3B!vYVLHtLh2& z;)Wu7qUaLEl(U*&k4w796hC$71M*8X#G1VrrFU3j)~TFj&8yY#)=8ZN+Sg2*rGY@q6mKy}&UiHzu-5adJ`GEeMKB2};Yf9W1N+azC z;FsTTTZ)Z*joQp%6}wNmb+Q#tyLMsobdg5QdiDLVF9uZ^cw0l(fcXM~gfWmpAj#7CMc`|L_R%=4oYZ8of${%I_I%92@BvfL%#s zs1#^Ff9`nQdrWx4dpLPD^`fk|NCi&^1(gp5mr@Mhk#j3bX2pzSaR)8YQDXR=$zd`{ zyLtJYyolr)-)J@#$XW-~P9pOuxtKT}tI-?VefN>=PaM6Z_XBTk6Co63=x`k5dRF2p z=&K58-^{fP?3E|=g{8i=M9bH>2+ss`b|2Jw@R0qbg^w|Ia*@ou!Xb;d+@zIxq+Fh` zdJAlkBpRK{J~7W%{~wy_!DY9J$OVhRD*wH|M11wX8h%zXZ*b`KdK2;EMBkoXhw$e| z@r$!W@W9?vV{e#qRFpv8*w-_F#B1u9pF?8mLeXWw`&^Za=+x6k-Bd7HWWS%;tI+5W(VOS4LL@wO#%EH` z)Vv^j!7Z_kwApN6WSa6eRemc&`>PKmLmO>3qn9ZDhuIaMWjnAn-3MF66?PLEw!x8T z%FW9ESW1gZjg7nV-v|sIi}yjIIo7gpC;6@W&7-d+U_a^L_rr&CNC7&jKGBZdS+WRf~As())Q)w`QM$MB9+jh z(6RgfIIkx!0yG(>II6C|cRn*pkb!|dtc3vbZVa|l2%3L`yb1-$9Xw7xU;jO2cJ1&j zX-e~U_$$jSZ+?1lG$KzH^Bg?qAxtlH8_&yrVmhtiasSH3i?+6z_vz?p8rU0TmLG8q zQk>RZV~H2$w@<(U`5FH2g0D^eV{n$*J81C3O5G!w1Skg2BiqvVF0}-E z_iBJrCgUrl=OvCun^eAxs-3PKBgFIP=OUbi^Bv0!`-V}F9{F4!EOq$L}O;(-J^OtD4WxBrd7 zCuHjawPuVP>62m#X@dpE5T5-6`hDJ<{jGl;?bq*n=});;R}6p}>&u>aZl7zK-l0GD z1g5{=sNt8E#n8c_>5rDmv60Cc@28l>(Zj|`%}*#RuFx#QF`u#1xX>RsD`1qdEU=w~~d|GMliUFh@A2@EGjhvrY#b+CLj z1r^U@KYEO_HUUS?e8~I^@9g1DjLD8nz?8HP)C%Kif>s~(%qgh$B`hv`0i881Q)&VS zE^|46Bq3{7(Hk`$G}V%qv~4(Fo`;$zzg98?I}Y)Dh|R0GA2aVb0IsmHwt-)yaR11^ zX@eNooJpb`>>_@cn1( z`4FJvZ;5VNuOlD|exHPeBHqRPRlc5d1z%67^4AEYbd{+7ilDJy2Ht_wl$0*Dp5srN z!FfdhrtU-GcQ*8*C-cW@1s{PTxD+&!Z3>ANK2{>d1AzOiL;OYRv|H z2@YjG$YO}P-Rej%R2jJN;p*)Wx=KllqKW97AZ=(*eQxpV17A)uPo#&6WO^{R_M%MU;ng1>XDOMObPzvQEr zzc@j^(10)GqT_kR>G}W(e;~ZT?^~sFb%PrIz()S>MVa9cK)$>e;Vu@Nq_W>FZE;rKjn}i3V+Si59(?CT?qaeyT8zB{%#b%Q-A9B2k?G~ zS${j~3Q3^>-@a}W1gQPDNC_{0UZ48^dyHCB^5zTz0{|ca8vsE3{~ine|02{*rVjQ_ zF0_W`=1!*OhAx)&cK<&qtm&$i15yQX=5V}5U*%Rl#9Ykpl}e4JoxaByg7 zU@&3)J>0|aJ07ybSUuB+bN+T4XE-5p7V6!aPC=g>-W``e-qU7>kgv~K zNsxg{*t#|OTjm6D?e{Tsk@koD_7v3Ao_>|1rChs3S!)AlWhRFdVYJR6wJ~)9X|6IU zE~#lLAd{&RFe#^=yCV)%u-^@`^6qk;G>GlSmLwn&Cu8L!6cQT`$=B75(UJ7Tq#-%a z1q5ekwB@Q6x4cG3ks->RbAp&8!(P8`QuE!YML>a(F#q`NC&K8yI9V`R0HzolNG<+U zf!FAQHYl!lnZSnkPHRN+crS@{=5_i&HJ;9<#n>`q96|~#R{xhZYn0Qh?h(XtDV+1Esu5+%j6bpS-fT*X9r+r zcAh1PJuk0F3J)!#O*|VG*Ez@#y`Z@Hb;iH%&DgXE3d}1%(E?GL%f-jNzLyuN zu}@4gQUNhuf!GUyu!8J_AgBEn7&!rkb|L3$ZET5Z=yHR~pW~Z|w?#u<3X9gIwNES3 z^>x#UEe@}{TE9GE^FF%hbP3JOS!R8Vb6r>OQ91Ks;o{7zlaFr7w`SFO@E0*5HbGf~ zLM-!2jwXA343Urv2K17Sy#dA!R@Xh8v(dJh3jKk6uiy>hPfH3_TwQJ+{S z!w;Gjk_Q1DO!`)|Demdf1*Jmp${nLo?(S-~C&wC?@Qb{ttwPevI1`X&Fhf%@mE}CR z?zVU)b=|QP*OaZ|#*I>vkDrn#@?xPyQ?Z&?l&_H92pt1k2wR1SC&T!{F*X=BrCB!SslL?YH?k%&!k(nX$t5T<&QkX z=uAItN5Xgx^_a)x-i1iGVc#Q?K*E`SHIeEeLhVTflN0lr_K+ z5;78w?EIj_B``hq%OP3p5iH934AxYc1_ilByWyA&Xp(f6cCk$byNT0lLDe9Og_7kr zr|Q`vPmuE`N@Om(9rpt1>4R^ljKP1zW4Jx2zDFT&%#?M}rH2lus_Z`UJEFY{o@=I<-3!DGa(Y3%&&pSg(vuc(Mn9@> zU%~fN(gJ6x)t+v0TvTQ*|HVCl$Y=$RR!}hVEiflCE3++?O!F8>62+mgg_6-a;Akot z@YKQ4wzft(+6&8Niv-=z>~E@P$J@%Z%i47%c7>zJbISD4Yt0KwT8z%La5y)(xL}md z17N5+t5rU?DzRCf@L69>c9!o5J)Mv&J9%z=MElU?4$jeVUS0b559pVsa{%Z|YDFGLjz}3S(Pa~l_^@LCl zz-ig$&CSHfC$rUckA!Z3D080`=8F8c(!_6k8N4ugXqp~$h!%{NbB{v}`e9ok&UWwU zvYZyF#Rk6bJ=Qx~0@O8cC!A8oKRXIO_>fjEr6KXu!%Nn)?rai*K5Xq^LBukai z){0pX%efqIC9kH}iUroj)aS!Sf&@h^Zzs?ZU{E^=jZIgQ+~wL3u-kc0Hd$;j2?uu3 z$xl=uSJnkaP(+^1&2`Ph^0hH83#kVc%CC2b-5e@eKebs`3%_`VyBK7UIf)kxT9U%) zEc3n|`{7B}6Lj?->fq9DVBfWVD@Tcd*nJoS0~d`;HZhk6jiO z(cEpM{lm~g>N87Btpfs9(y6e>8>N&7HM;e{c#M@Or%sv{blnguTm&GImgfFNq zm=cDqhF`?*Qt2{+6ypV9my2IypHa|qDvT^PB$G%kZ1GHebkUl#yaP+!(L!xgl@{>U z9x-}7h+Qg}H2ZU?T&RiNtWn59D60X)Rt;}I9dlf2Xs5CWzknzTSchj|+qC>a5`*(Fe+G+%0%B@-K z7lB>z>!z{Zj_9P}w&_7*n^2Y$@IPu3a%tT_*EwKlCSk-+dYA&MQl67rUD1#OWMI!K zo15^xWKD8W)$dNNpZiti`H!sEk*uGrv?RQ-p2hd6x5bCF^d$C1dscnxE71h6)oBvKN9snbrHeWs#xEo8Bb+w>Z z({`~pbsQZfExX^(N=(4PfR~$fgiqx59r?9AoMKv89(`LGPYVD?%_5rk8#uERbZ8S*ni@(k z`wTs3f8C^IQzb=j{1+-dZl`I<3BZJ{pYPE@7j1cp`VfzqO7FIvyyT9+uUnqd zEagD-cz*VQWIH4#oh2(R>|i@2Ebx5)4-uLL1R7>br4z8^a>GUXV=h~IuY`RwZNF3# zJfM+Wb(z~uru9Xky=94W`=AqKZ*%!F%eZ?G#!CP9h`OI&wHY5Ij{2vr+_v99v1{%R z?P}W;`w7_4onc7d8)_O-3?)@jOubdc?AXR(;$Ffd`VMIHZ4#P#lI+66lZsL%!5zI5 z3~GWp1EQ-(!^+eD1Q4VsME8fFPDO>#Vo#vgFo65VM~^d3c8j6y=wXMYj$%mnFK5*c z^SW{hO`i4{a{rS0hu+!s$8n8M8{%ZeAb$)H>Hj`>wVJyD zo4J(f|7AY(RqcC3Mau|IOK3*r9$spO$mxVdA}+Xr0F7C#F?gDw@Gvsk>T|L!-i&s5tq4r=Nw>zb7ORb|IqUe&kYw!OAV zhiCwg1k~$-nM6_kA@p0KsG%%ssp3Y?v9+H_#Us~1pd)>+e zdo)-WgS-D{Z@C z^#y?s!XE)MCRUimts#n|0coN*j1NdDT81piny@vt#F`Rksd$>Br-L z+hqa|rXZ!!bEHNw#li$coi*ZYW{;v;GP7^;E}}OeU4Z5UL9A`z?mKrM>Q{~BEPBLpf)aIWSm0}`F8hZ~Qs;Om(H(CY& zzOQ?ON%n9VcJR00hK3on;y2LcRAN&mP|3XK8FpacPxbW%jg;vaTHgDV+w1w`I{mL8 z%g?v#BP?GUMN}8YX{_g#tf3xAO-&s}i71guifp+qHXMh#V-t#8xz)zxK?3%<#}Zuv z0KjRd!uXw%A1FdbhE2SOQKHS(Fy}+{02)(V^~X^N9oWIbQxQ$PV@hB~1Ypqy7?HnC=~PN^0g5 z0v3WB16z#(-JSF+2<2&s05oQ6^Q}dSEuKKziJmP8WBTKS6jLe~iAaJUvN+uU(ieBZ zuxYvDvn?YhL*6J?TL33ACpbz;-RVqzRG_6q<3(Id<12h*3^Fe}+6dKK*$5q$DZ-9X z?_QxooO^)t>5`%~>nQbt-r{fJ0ogsJ9h1=$EVAG&8=Vz zc&J9w3_Y~za=QwKnV>hh0UgW+tDTe(Z-rl*QAg%?xnTZs-a-v-I0r4;BS&GZ1&itk zA0_D5>Ca?ou@UW%29td?q4_8O+jc`Tpri-%0G}jdq)VnOV}n0zm zxSCodEFFjTn`p5v<^SMnO=zgD=>H{Fjve^G{hrx!W;ESGTUeL!Yy2Fm9xH5X=ZI8v zcp-eAN)I>AA#kplA6rf-j%mZ7fE3GAL2v~2hB^0;j|65|DAx$%mn|QPdB%?saS?GT z>TyHS1RLhQ+O?86NEmrn(sjHl$*#m^{oZP8C}0CV(4A}g)R>CWV=+VNCGEfL9-^aqSKFpRgVn`Wcz+Bm1*k}_uWZ~LK0=erh!*REC@f2;g!;95b;4gNEgJoVw-M0W9~6)SDt`W_O@?gJ#q3KyLmp$2037W z9&^(InMRfNX_fSr_63sdQTUyYX@5YWmyO|79}-6vX_*@a7(KzJD6PF=x6*_5f@ZvC zFSOcXZnR7LqG+65{k-FTyYFNvf0Rjc^7Kkog7sh%k zWNTcup`+8Y%M7>tY$}$Z8RRALP>a8!d68pnN5JV50K3~g^^ZTo-)Z1M%?!+OaSJ8J-+};!=}gqQ#(4T^@)>SD3yoFi~tz z%CP;KF_@#4)MR%PC(^TqyKqw$yJvVKq$-@%XQ)XbtdiRQw{`g;eu}w|rmsXZRAPyu`1H(!w(Crvv5Q<}Kx|8iJtJ8AWGNeM>pu!lyt z0$Q{`t0e?Oqnop{^13=Lpq*k7FT1<5N6=XTR1S)F?(jaqw!1d|>PY6pqHlo6qwdbj zGL8{nysG$)e@hc^bW0KC?kTl}Ew4Q&J9aD_dR89~<CQGvi( z0#_b8?cWM=Ii$ok0`s_|sV|QkrcMB{nn_x=6T1e*p6=kTR49jr?-DSDnN9-I&~7Jn z{6Q%3NH=T73VmkH#X_ayg2*Q|x@(xA)iS3wS<#h_;rwtKeWT)V=O% zs*>eQYH?MUyj0t6G*F!Nq8W(>pjYfOiMVMb_K{)t&_{w=Kxf$5&*uz74Ws?~Enrk> zpqu8-^|xFZEy;>>ry7>7qHb>)Z)m~S2biuNkbf`iW?l9dMM zb>W3P*Nb73Zmsc< zc$YnvakhcujrnPMJ2Ba|pdvI`3J267ANuHz%cRgKPAuu&KjuM3c570-#Uom)Zmc`G znpmXevXikge*^Jdwbut@=W}n4MsZ)F8`IU0Ik7DbGq^4@G>+H%^2NTFBpBoVcs}#j z&ehlJnj-&N8zE}L6-}!#qvyS=ePWt`_gG5UQU8&p_0Dv(V0D$j*Up83%KW3}5tEG$S z|L+)bxKJDHs=3(%{O*iZ&{iD-M^XXX&75GH}9{zZrz!$CT6cMj%h%zrfCaDjk8K)}F2|NS9;?8Cws=f}FSVP7wuxXRZl4m7cC?HmF+6;IDmJjBly<_|J!7W;NO*8wbF<2GKca0H>lB( zm9eG$El%<1AD+dr^)1fv+BcYc!&_~Gv-2xTGj3FL@OWpP3;+S~)Q6t@;-b*z;H>^s z@7SjB+jo{ReJ#7aUHa_v%@C*Oe;m_bW*7Jhjv1^05GtBj&!Aw?f?`OafP_l${x;Kt zgg{ps-nJU@;hKAyZA)EM^88Gd4&BD8(Vf3?gM))Z1)8Olf2oyKRfU_y4_f-MP$_TrQJxTk31S5Mu3yu)g{!i?;f2Wn_J+M<<`Z;LNRY3FM<@xF> zmf+W$hHX-Z0Yk0*RaByjEz^>-`(TFNHHOyw9L^D}?He{!1j^(^7sf=Cz@0skk-jLC zE7w^(gRspjA6Gd|f__e@emTb$t={p*C$9VwonBGxxQFA9!J&Al1AI4Mg4?9n4@72> z2A_K{^ToTxvbv_&lEY2p$E|*Qn-w9}pF6_~9j?PnA($rIY!8k5jDaDmbchoRPrr{g zbZGFx$C>Tg$5ses<3XJT6Mai5ti7j9lB8MBmsKcS+DM~x;8GReSetoJJ9mAbWy@+m z?7ft5+1J;kz!|ue=E^SrAQZC`$=0Et?4pMa5#+u~dA`Q10ikl}sK^lq8JmBk%)Sh-il%)Y&fE{S*8^rP?2 ztG4Al^N`)QY^|cR5Osg@t9AoA@@({NJ%iD-Hb*~} zaf`4EyWu{*A$Ljl?xI@Y#>?*Ir`U9FtQT+Lu{<*Fch{M+8my1@L9=I2$$t{Bz291% z50XoFH0>3jL(r0*nb4V7y!RY?k6i5cngsh?Trcb^J(5T130o14cdD=SPC3mfsB6RJ z+Z^;YNhQNtu7|QB0<(Od>*>GR?h!IKV-U9lrX>!9&EOXk>scp#1A*e zEyE05Wstv_F7+Cco?x(DgV}~9xn_%Dj59IPJ4C=F)QdaD91ZEK1A+-q!A*r45vg5x zoq@$gyi_7Xfq@p-A#Xe;v!XoaV1mk~bBe=d=0>+?Te=8nm4GG{6hAn`rA8Q}pbuJs z%$K_iCvzPX^d=65qK*ZJu{q;!=5aJ_jdPAa6Qi!d;2 zA0>TbzIB_)zcXMb#s|IBcDV3xSe=7eC;0zsio`-S%&khzyEa=$IVUD@QljAH#T7aD z=mBXP()dPHq5DFD4WiOCWsAcD`DO)IHW#Rqim>kk55J%;0O=DYE{$baOZNsgyeh^ZZ z3-QTGAY&&xTS=I2`@vJP$?_wWXsJc(#2L@v1Uz9^dO2`iz$44DuA4kulU|!xsRElC zwm(7s=(YEZ#G@KX@{3lfu_V9sjD{Aa=devLtNvaDHH$k(Xa#}3662<;PUp9qua3{C z-@6>cJO?A`+C=)+UxZ-LFRgIWB$H;G_KWi8|C*4Q7<>^Qp998>vq!$1RkVzPsq58VnjU@KqbDW?(PGU z%r9wh#D3n`Av5mZYH1HSA=~x3p2U>B%PQUxdL>6&9TSXj-3iyLA88oV@{%Tlp1;mr zYbm-7KuMzNqn*s(<}0`zyh4%ts~?qPc*-2H%XW6OP`;)eky4DWWVfFL-N0JMKEr`} z++eTAm!ySwdp|tN3+Vpa#TwLucS$2;sLz<>RGwo~_&nZKazR8M+llaLcRFb)A~ZKH z>L6CFYlbJD>aIt?42M{_PDY1)qPZRcG6~o(C~b z8Ptm!Ofk{p80TmB2?4h@)#u>n8gx6fW70=Rd(=D<^-Egdz5G#Y>#pzV$Lv4{?-Pil z7u^j|*{lPCC6=p`l z7l>E~Hj9im5xBdb6nazf)@bnDd!Xp3a^7t4IMCC+)itfn_({O-H@A|@Ijg@v^DDA8 zreU0WlpeV#Hhemi+NiO}%0>RoJ*CcqK5_ZIUe{d?&ex0@tXC)8queCGO(qnvU}Cvo!1^oLAe#V_9Gl`8 z((0$#Pr+Y9RKPybHDZX@&=f!a_W-WgUm;0-YJNk+zMsSnF$qqu@VX#h2(W~lldN$( zPjgU0BpQg4T%P7!_DOpylEe4_z`SogU`MpSAC_PR0mE=DF~R?BQt6jKj5cmnbjgT^%Xa<#E5@9uu!A!fwtk5m$Av zyejjRLE=cdcoP4Gn=Hq60@vd1a$^?drdsr)Q=OHmHB*nm#fb&4FO4G&x27dg6+dza z%dLQJvyU@`|Da*r@529($8JcMIj9V+dF<&bQw`8Mf#AduT_?3KNPuuYbG$6E-Hgt#VW-T@+kRJ+}daz`34x;tkQlV4#b*|?UZHG2 zqT_kopn(L*mS)^+7B%K-;0bA_L!f=pmG~-uf1z2B{Oy~GpZanzPn0Xk#o};Oz)X{8 zLZh37o<;gSRqPH&g{j>78HiU|M^KdP)LDcUKsZg*r7$Z$Ftdc zdf|RDMD9zUq8d4YL(J6GO5r%H?aZ?aj#=}Qkw7*dcTF}DfKUgW)iHt@G(YoTXY$rt zW>%HYW|Gz#+F{THxeAM_W8b9ArD(_`_ryjuShWb0Abhd%OOo7_Oa+1Fov)Y0mNw## zWUCvG_~x!@QERk8u`WC~Ma#f!T`D{FHV~#)ReG)fj=Pdg+>4qHWr_F*x!8=+yCcu9 z1rj$bH96M&>$=4iO#_C8L5;7ROy{b^MBEV&`3++yliD>28}0F?;&RY5ELysX#D=oD zQu@cOu=Fxhhrd&jmPbU?tM&#UHVsZeLmK|js4nWZZ{m;mU}lfT;Zvf3t0DU3f|6lt zU|g!%T2=i)do}ReRpIe&FG-Pik3U~GC>9;p4LY(}z`d%XxXo+a<@q{H=2eS3(&|Ey zt_*FY5@D3EN6zk@R+p#6v*t+8u0`w6R%+yonHILFsh*-pwhOIHx6+Ez3)PS%g8jjh z1&(DC@qW6>Hn_4P5zE&UCzF(hO~wW9nQSrw-yAzu^7I3^4EXPU{hn2|uc@{d@KrLT z0LN1QmNMm>a#>S>SdrGRel9bi_F7S~%?M@rWc-pB6+q=>{7rGZLdL}(kNmH9UvNOY z{q!Wc&SH(rkhjFfDSqLy#+Zh zz%_9B98_UpzYs?j38Uz)lEWLS&a~=sUWaFT?-Vc}fud=K-jKJx*}lBk`Kt6F0ns+6 z*Gr0X1@bvu8ol$w*|FS~$U%~*QkDIfGpV4Zh>8Q(GCm5!WsYAF5gnN0_z%>>aU+Gr#3LA7Kk*ni*Nk zvA=4nNv~5!V8?*PQMM3PSA?}@6mCC&1Dqk8ZMyvy4h1%x5oOAwT~~1;)R%qYY%tv2 zLO`uqN3kN5d}66=&q#`fUL|E$0Zx;ESaYBwS|H@n6gU+?DeY81-sZiRX9Dd{UKs## z#;;pSzeFN%J60MAn#|MX6EDRI8O>bpIX1ScyjUtaNMFj5n&G2tgpQNBAh}{wxU45& z5l`U<{9cBz*x95&b07a8gtM9M+v1BEg+$+Fhre8oJCYI#UcsXd=y*>B5%1MG`#DVX zBx}ZvQ%{2E9R*hbkOMR6=J;4%ducw(7TI(k@$5qS_51DGAJQvv>ZMf-;avq9Oj|n% zEi=2B5OE-lgsEE0R?=pZUg)a>*&rQ0Sp%%Px)(GA`kX>FCaGYPpAsPjK`=QrPXkXf zB%iAdwj+HK#bbf*c9Nw*QjEm%zA&2YD;6*4um~Hq`z<*-aMDLtcJ_@McJ{? zgoopZqP^OWZ#CSsCG;nzuInI2Wvu?@XE$68I3Ooq;>|yhp_7Bk_pv5uN<>sLBa=EK zBQDo#B>v%f;$a7f%<>t*FiTWfcp{9`1aWKqXqDb*R4rmuk0MeJ#x(k=`Q<1wlnUJ? z9fzIHjxJ<%T>j~jq)W&&>lNzZpws%9eEDr#_R-?uByTL`koS#8D>`WhK6F3_Z z^@xLomp2E|U8V@~#Grv0+HrKEkb5pQmjaY=4!?H zB4wKDQ)YOP;d3EP`>V$=Rc+WiCn`Ms2dA@urjG2Mlp0>vBQA$UtYDc5qA_rL;64`T zeyfGIo-HclL)y)t*O%dr+=})saP={vaa~Xvozz-hD?KZnD@}S+-%ojMs+P<Mz)8OXH#2;uHtHzn#IzhwU z64s^Uv7fnE)GSmENYK;*}APUvz%! zKvF3rQOA5yB_RAl2un0l7(B7CSCoTH^ObloOusYNZ~HAC9?!d}o=gPPA(#QpT+a@z?H*r};J#+f3slTN+Y z>o(;0t-gs8Ho*SRZ*p{h5M{2LN9D^2o+382!HreApL2n0{vkTi zE4V51q6$uNQU3<|WsbZb>;Eej+Zq-)R~8648o-2EymM;PXUJM>X>zQt{ACFL06!9s zoldfs8d@HEz zB@H)7wGX#WiNvwDNfZV5fGH?EwZ|t!4k|KouxXKS*@m6qv-xjM*keVTU^WM|Y**rS zN0_VuGQpZ+Bk_fRB1DnKbMWSKykM zLF!_$nnT3yk1y#eXiFsA)BUJyrBc)hhjKL?C;|b`;9>zXXk8OPXae(_r4NL>GvncL zqC9a1rwoH7(-IYuK|J9WKG}=IS)u44+YJ!X*st&H=*1*Aj%mi*3BDsX`}D?17Q98jG#Rt>GP29*fzDiHiINeTa^Svplyr+EAfC1<#b$*zFbc1N;L1 z-jbs`cGEx4?#dxhKtg`}{hU^0b!up~{~Y~_j!=%lG!=xZ)3SNH`P7+*s#Ox0iL@EN zh}^Ls)!u&AAVqDNGxg++@7xpZiw9Rk4rf2X4v*W&_qs-R#b+M`pT9)*xh(0Uu8cI% zAawF2D*A$Qba}mPnpoG)xM)c4p@R)z@b92*-mz%4LzkvWU-nNmzV1TS)%`FAqMR(% zpk8mJ#ZoW#v12?_4RT>Ls(HqZD6h>-Bi+?g6M>@NGkO-A zZ&AK@avt*&18+@2ccf8MU!&zhe0CL3Gf}{esOVUfNQGi}MWh~8b2y?XDv+qY+|GKE zID%@vfeEAf=Q@Oh8xTP@muAHoOn#ht-ngghm zQyep@o_i-3_w1cdtu+J9qT(@_?h`(^>LatM>UOi?f3`tr0*^6C26hF2J7^n;4DbVr zb|}_yY+0G+D8kjZZ2Q7_gXPUn&&W~p@L4BoxO^@$l`QzkPtg@brJ1utllre+TphR`HAGEH=j#Gjey*@3oW zHpWO&bYr+PQ3}y4>YiiBt_Ef7jZTz+vuwZ|Ze-NKGq5pS|4M_0X`%|os+sA(v z=?qVLVWa8`$ZSf(Wl7ME(nCCbB<1tnK%nc0oy;!ydbl=5qXkRO-F0xS<9jbTQYGa1fkOM115>jH0f(S9Ae6xTn=VB$pCx z6UARs5vM8!?b`I%53E*tccO12* zl#d*o?%@Fl9*DQ97tJ8fv1RIvg*q?w6E2ey3=t48J_7xf;khRrqaa-GtxKB2;pj4h zeXg6`=llDco=^J)`>oD^GQ+$lGf}&j`XKmu3b5s9g69+mJ8&3gO{kgt*N z7=tW^4tD;EgmzDFkt@ioSyCW>XPk+&8(VTo2GK9qgx*K#9s&%r#cf0gRUfxj-ewhE z%>E$?wI<|MUBMSu)~y)wyuEVC1Z~CgLW@GE$Nnih^?R*$nl+4Q8ESaBo0?Q>1NE_W zJH&hC6}b2O3e1O5ZAJjUn`;@4Z&hX%M7#S}>D4qE@?>}-sv#OCp{p_)OW`cOaO{A7 zuly80W*PEe>^-JrG%6$bXwa7mwJoQ=ru^w;bg}y4iU)=I{R)bq>kvWVTKmFP(1d>H z-BM~Bb>Kv|`>?UZrU{DeDt3d@65@cbFzY;d;LyZ=zj4!OZLm3xSuQ?&TaDLLOO2EA zk_%zLggX7gTbzL0-9a(b-xm_U8~nC?b8Rp9P+iI^V&BRA5h&|sfpos~Cpu^0)_eC7 zt!kqhsR2#lwz%zf2VL!^rM9M3jY!$LD@2p83;?cWC5&OL)Plec7H{h|NAeimZhC5j=Yq<}AWaM~>rYj3KaaTZvQgIiQ(1^mLm! zNuhz5CJ-a{R8Tp8hv!UbX4%KItaw3%ZA(|1RWqaIYIYC?=$G~D09p6^Lcejz&bV`|LhL{g;m->|Vq;G>Nv8R(N` zJR#E%i=v)n{nH9SgLjWiF zgZ`yPO*?uM^X*7!2@^%kI;MiGh-y+bypiba4LwM^=w2FX-bq11!ssEyyzcn8E5M@y z)r1=s>gGhj6VUt#{JKBpsQGgV^aBA3s)7L`zVnmM`8$}h^H<}KE*!P++i-#6UyI;G zNbvO3vy6oJ*Hb{$l`~{Xsb8Xm&itd!( zm)Rm9>1=|fT?#Bjk^V!(k&w=2Hi`vSzDm-cnt-J3Mrf|AKrERe~2o)iSK-~{CdEN4BawXibeh0^rF3ba9| z^8Azo0xPK4nk6-`Y54XEbl6D;NpOj$eZ$}=g5##@tJH>_^hUp)= zvj12A+D;(sRuy=0^QV|1aFtg&5*CKloz-YeC~7~)&D4sy#i z2t44GVHLVbHb*S{`Jw>N`1N>?bI?7Z$Qdj_()otMX}aZG`j{&%y+MzYFcpzwfk3*$?XTFI#CO~ z8H_p6#;nu1PTt^pg0sra!EL_bmHJ*-KJr>A{;fp%zUbEK3Tezc*>fA;z(wT3PT?VN ziY7fT8FOn$kL^XVLRDf*!LEjpn(b#6tCN7Z1@{n)!j{>am^xkKiB+9^=oV~&(dx^c)w3P;vLDfrxC#DT_D>Yp8OdQYzVyFczh0NwBkHo#)rNW z@@=ma#%0c^u0#enrE@_5!uXY>zj@{?Bh+4}>2Km3n#{yY>aT>$i~(R0i{)Trm()pN z>~oUi=q+2x}rQm7*;Nzs8$}MdBWAal_^5ue(wTYaWr&d1$eD5V&lN(PYiv#d zzvLvD*@iH$l|{FmOc$vfHzc_3OC49-4(O)}kB^P|cew?+e$#%3M!^PWMZnEo$} z63SY*YBy8YqT@tE(-IFE#Ef&jO!9(g+(r_7wtjeh{Z?wP)n_U0V{PghVp^ZlGL>UG z2@=Uk^X;)7?yi%+_R_ez86prrhp{|in1$M^ok=}g0E;mhd6SHTg;gmZP4hc1gjgfV zw6^~+#J}(}a zZCwXNWF#39CP1IQ^`FrOyU9R&h?8Nq4X-{HO`^Egm@9Ywk|ks$=+G?fMAtRghc}*+ z_8=*#OkKFeOUT_JMHMJkV91WPAC`X`cu@P;)kKxE8;({R>`^;YH!D?_rv|Ki@U4Ex z)nKnxi>Z$uCpIQ#W@;e_(i7bRCkdoj^bif;f$HXk+EVx6M@6{@pKB`&^BP>;x}AS(InRD zi8X>mE;}nWSdM32oVn)ySye~8Ny%}L@Sf{5kyn8(KsJ0S=2;wA-5-(56Bral(Fo|Q zVu-YI;rtLi#b)YO!JBRYS7Ko6pkwYB6GZFH3q~iRYFJ5kl=KJ|=oYVuEwOLdN_9GB zbGe3=a|igDs5L}tX5D>dzJ?RS@JTG!trozJGMi2QmVy>HT_(sN1XTmYJ#dqu94{C3 zrh)khj12U8qlC|3kC}Bu51DNT`iZZ51&4CrdqqmPu&zK{wBHO7$Zs4T5V9g621WgS zSMYZ@1x=7~Ghh9@@x21_{-fk><`3d?5^a>Q&^N4&y0JMKUnlL1+vHVpniRq_Uz;X4)JNpwE+ z?|h8NZppVviaQyk{9>oz&P8c6Zh-j5Ucw*7l`}IV)f4P`xfVH)%i&O&x|?5}NMgKN z+pnX&QGaWFhGM#dTNM1}1k2kWh|{FJEqr}(;tTM+4t93%f3tE|Hh(DLrFOXwq9MGS zD&C^k=5&GCHw`?0B(i=2u4Nku^+@)O;lA7wJS7Waf~8Xo2iPL7R_PkuqGwHlqHarQJb<)+xdcn%4n6 z6SktW!axTOsQ5?_3Kw2Asg-k!|!>V(F|SqOI!isu~4*FwBct8(BNQk}7Kt#<%c^ zs)}8uE{hnO_)4sOw5jYq7VYZ&W+OZj2Dsi}eZd5nBRqXnxuzoEcA-}0Nh#3G9&RX# z73P@f3)St+X?)NLUj(V@jh{Q)DI+ zApR);t*rm+5fH}l${9Lk!}BZr?#XAvh~epX&!epKsb+D{#^NRjkkLzzq*2xVuX4d$ zApMZ?>G=#7$0amjgUW-sW|2GnA-}iQm_sIe0EZ8)HLEZDl|o+=Q>t?INTw*}3oL$S zE1AFZc*rX;N0l@x`G&D5h0>bwP@hO7Jc@t#HGo z0PXx)>Eoua7k;r>MYpv9@p>Dq;!?{S@d}>#Ka5h1>$n=NyMZ_;=fB~et*hS4uvylGBsJ^fnQ%Yo#1vGm&6>tf7401m z2_T1mOn%J!!o*tdnR*xTI22=%D3_ ztpgo_2{`CRn(?HSwr?nqmQvzlDY2MTLxy=3yu@mTvl#(kxhh`W#>EK?mJ)U%M()`jWB-7ciNY7>7uO>oMA`?j8wB}^ zQ8)%C39wQuthrF3CIDwgnJu`AyonAR)#W08mPvv_y;dV>gIX^tf>NT2q3(zBU_kt3 zo2WPycu60|emtA}-YY&|8p8)%#+LtNQK2Ske89bm8W$e*k6h0Oo@YZl@_iuLS#F04 zjmz0Rxmsp4Py!w46bqhND)@}Z0KWE4vV{_3M@e%AqgUcaV%bciz(nS=4m$VIFm2)_ zc5`vH{4Zpfgzf;@Gxi=5mg=zT3>2$_-0Y8?f81N(@A#X6xw>MQZw%uEnU4=Z_DB731v1yh*tYoeJe06NPBU=eX6t<{oOcPPLh$+|G zG-}{EssBnNL~*<(hY?}%Q3cI#%KD5)!}fUsUecQa44wg?_#6Z!+)gh1`sLAGUjCci z@+03{^qdLg(|9=Yx&=Q6NN;28b2gDdJ_U8>dI!=vZwXg@*Z+zXaze9#ir)TtR#aOR zuZn6szvi96?dGtkXQL`4%O0X&9cULxz1iiwJvp_NqI)T~X4JrNM$*W3a|j}kTcK$I zUt>gu)*b>RD>+fjVq4V4A=IQc=Y$}c9M-KRADZ{{G5y?D07r|E)}s99WWK>E3#I>#M|{-&=X2boh&(bPg*h>1 z|L?bn#1n8G!~sCM*=N*8(aFZo?o zmt~cZtH}U)G(z_0p-JLwQF=wxRmT=$>c1hsN8V30JI^b>{HsSdBI<(9(O zua$EY4YNZhf;tm zMIfRotnYw6bNZK4qrftjL!g{8EEi-&UaRgh)V-YdIXLB;B1Z5Jbj_3M)U*5EqZSGz z8M|FNwhyQr($<$>1!jD`99dYTimzJ=ueC_ZuN)D-G{}7}QLhjpX9M>c%O(=G2ox_f zgX`g;M7s!4ZYW%ICN6!7QK@ZMaqWx2XI}$wiY&L;uggwaJ*wocL|jhkJ9DLAL<#k< zNJ=vbmw2d0ke51VnF~^{1Sw_;F%@>E++}F`oUO6ttw>&Iv{fzz`mD)m3MOR->aal+ zwAd~Zpr}OmZikZUti9M{-yrwi2zJr8=cJwajh;DRJ$J1_Ivs_=wLT0d^ga1#aysBD z?>{t_Rnc7v#eSlL^7jOSY8zM-koh!&T}n$o13Vp|-u!n6U6RbB5Lz7;zov)05=zZ# z?ow|muKa1Cx?@u@1CGjPw>y^8gIOr#X)@SSZCW+RYj5Wed}{C6HDOppC^%)2R)?U^ zviB*+23KO-#FNyDQmu6)kH;SeUzdj?I4QWNM;w~|=9{$Y4}tvs8Vum`oBZYAl`7BN zBXr_^YkmIrxWY%1qOL>(xvC0-ZZp+Pq6V94W37WiXWHr&Fh|pdvQhoXwD$7~#G!zH zUw7`liz8u(qNBhW*6KZ{be3pEl#HYJu2#syC$%k+omrxZZFrT)@3gJfXR4GFq@7tK zzb0Zm#k#F{s-bVbUUo3SEtAEU)28@ z2AN=~Cm2p^RW**;F6qqI9~%+v%Fs%dJFR3P{OoI)x@wXH#lezr7Wz-h-n}?c}1>MdA9U|b-|jVQrTSO|ExWyL0IFc{NSyaT@N5ak_&^vWsy*AZ zvv?c@D!oH@8`Z2Q@5Lj@RNK0lnprl`pAMf30)bd1on=H-!PnKF^nxb3i9-4XY0<$%d zwt%dlL12YUkP!lO>b#faXny$K5pBy8{*N_q5xs}Mo`*V za)Bdh)A9Iy)k@oiI?hnb?PRE0xx4of(IbJQ_dsI^D~?^~<Lttq~E_;HLuX}cu+y&3?5;;8B6! zF=A&nI!i-91bDVlf7}>Kbq1#<0KF*kwpo~yq z603~J{IDdGtS<0KHrd0XWgytd+egV)2W*#+>Dz*9JSAv_$6RQnT&G*1k4PieQ#WVw z^_@Mf&(vSP6=W({cd5xvZ4%#(3ZA-J1<4z5v5pqgn5_ER$kk^IHq~zJKcJ+uGihhQ zKL58~;aF0z{t?ec>1{0Fveu+e2aC|Tj$LeDZ##7R9k=kq$FV+U;s9tyHcUY?HLA3< z`YR}{P1=~^SQ@kOG^VHAdAhol=^swmOURatJl3IfSqYUuTr{aXuLsaBh+B+C7I6>Z z&&ao(<~+uzPW@oyDFsRHlpLHpZ;3VN8)Beq>W}LpY&f`ROUGnBDPb32o5?Fq_UkKP z1u*$YvkmE9)zR6C-mZI>RnTd=hh2AHA;XBnAu#Pd|X34yKr-tSf(?(q?e?&$zm+ zKP}E^ay+=&$_>&qikyL&QPuC{DQt5B{2NdJ$s41V1-V0`VLzpqWPrr7O%92 zazo`FBV0G${WT2MP{-u$|4?=g!J)uv6mD;A+qP}nwr$(C?e^BTZQHhOx9`5yTfa;e z$u^ToCi%}fpM>|do?&Q3bummEHQPV2Djj*H z*6?d7-t?ENrK8uNgqdP^0TthM!37jb2)ZzhdL|z3?eGnO*VJuBwQ^|cgZCyxjI}I| z_|L=km%=J>k$z8R;LC92a?!%R+9K zhsTN0T6&*h^fyns_Z=T*5vuJih}8c#PK`D9vk*B{Jq_NkAB2~ zLkLtrw~bNrRWq}0YyDDP_O(k+S5dum2g8M`3D@6r?6`xh6}2CzI+-qF7+uD<#WA>i zrlxo{;p2)eFJ&=*-Fmn|Xbm7z>l#^MX=bjj!`q=g>F8AhArJ4TPUodJ54`mpHa(3Y zT?nDO?SpS{5x!5}IwwGPq#liX5noZ4-aO|(?;h!nWWf{R9uIzpI^GL?-;g;Iub~LO zDK)x$@8q;`nzE7ToXP#)J`XBk3Yi=S^FP%qj~qy77oZs3*Brmr;BEZf>kRkNp`=R!X{!mpR ze+f*mWV6ELd{GYQL{5vDzyCo>(=SPZ+Il58K{PXc97VGi0d6r{@8}}Ph*0KD33E@u zW(L@BzBVYe3^Lw{LND<|ggu6+(irq?{G^m3#dK-Pq-2dF@dKbGtJ6-_o&tHQo%{SS zZ)STo2r>?ZOs0`8@l;|;y*vh4^_mCn{1b&2muVG}Ysl>=7|p$QxlT0p5hCGR(9ZV9 zO1om(eRvVe#y_H%M)vzR`Q`Oj!R-2HRc({~=XAiX!H@3b=a+c@%c}T=&no{H&*H6> zLTKE=Iu-&RAD>wqAD{B~S6QMXlU?1NqkSVHBVFSck>$Flp(e3$q;_Tx{?$qRF6Pe7wGgO^`6hB!N$=<11MNQXjDpAaL^2K&k7p^^Z zydaOx;9%H^U8A(Sr+TI&n+X^w4}Q4fCx6isPfEa(T?MY{PjwS(wl1M1Bes^Z0uXpi@DbNcwn?F5=K3X`2$2`|xumns|gcUR;v18rNK^Nvp ztlS$AV|W@SnQWkuG1EOewgWhMVV~)Opwf9WXxar`?IvDSRbK znl(9nx9MhrPS6I>mALUlld@e~-R^f@g<{8b8si@+hGJd8!tAeOpA#4(SvUKULq0l} zpoxS`W=a)BJ0%qav^GGXXWzHe1-mA9EQ^k{HVaM!K-$b*abC()y}FfY@n!h6dv4Pe zB~l<0h<=s{RXQb>+bbLq)ZSV8Irs)37i7z%Mv==JbOPo>#}&(-$>vYJ!J^9M4N-Su z!&_tctS@%xnTM^qNM4XVyel2L=5lY5R#0AJ+yfS`n0T*Ev(5ma_`(jb3YajLo`D%+&9(;|D^0(_9YnSD{@ zQR$M+j^F7iTJ0#IX+n83X@;}mhLi0-Q*=QtZVl8NDA7#QK+uUT(6Epj+U6!&KHPR0 zbmz~!^;&*j)U_p=`Y^BU-Slt&YY?e>H}LrCbp-I*K)`OR0R0*LByF9<{CE2OepkM` z-R=7~H;td$U#36j8IR80-%A`S%cURHH-^#Wuz0W8mN&k$Fd9$n6v}h${}S=Ihu`hx zm%)y^IT9~$zH-P5*6DX8+jzEPzevfgs+@}2L@%8}yKPS7roV7nD29q(T1*#b#U`G- zfzb3x$>q%8gSWtd>zv!a0$i1b_!rRI2CNWrLXk6-Zb!`9Mj!8+0EtxFz`)vq000Tz zXR-6UQiGbYPVqjty>J96sQE6qJC#R1yfwbzJYA2$8%^qXTbd*eE-=&oqH}1h6`0;Z za!=1-+r4VU;tG|3SP}>{$`n9qwz%m#U!Vj|= zKhrt_EtVQrr>1>%>MxDEIoMOtnZ3%RSs5&VwA3Q7V-OgsKtql7Wo?K`=L1JqdseEXx(Z&m>Zo1U2>xd$uvB`<9#-7=pw`UkWrUMWH{F0JXE zQEDD7faMd4o8Ag5=sy~baiF8hnlGGac<}EWfiOI{I<9G!Nzp0_RywO{O+M{ zOoXCluco0PO5NoO<82TzrZ$n68jYY8h@;CWjUu_=>`w>kCH+fKnERW3qppC8tzz93 z`ko$}I7D_La zmo*RcYLy}e`^0f3!zdXtmSE{o-!9QQJqlWm#F*khK*yWAa;JlBA`f+yu})9h@Ys(s zLelEWbpmai)JG*hw z)@KWn*ahh-nzYQVGvFS{%vT5k`ys6KZ)@$g#mQPdsHCVLu)Zqn<5(eFa;@Y(iho-R zQpb$Y*-H;7i7-FoQ}T)Y3hHEDJ!h(UCd&wr=?(L)v}l5~!7B@G)M$jWoBbW@JoduP z09zDQvN#YXKaM1m6Sw17iPBAZ{t#jz%B(X0nv6bgvs3I%n1C^a$d7-WR2W1J{dWoKR3@~#cp(^f1dI}H$mIyt2Ik`VapW35buBg=OqN}WC0|=(T9c|| z^!YtF69!HL;>mp=pPlKuC?W(Z?~BfqqELtd%46yj4TI1xwlbeZUL+kgMyd2}KRKDr zvMVGCmWDPo41UR;o7drnFRJ{RmoxPQltY0~Y4|>!-I`qr7v&$x)1Cof?NCj{!iF9G z>DTy-BU~XRO_jt<+X1cy#A<hYWg2X~~2u=wswC`J#!W@p5TCxqop&PyIdK~SX z?3UjvbJ8mP)Ky$fesOT{_usMkuT>P4!(CHdxK-`nV_QTc&f|h%fIj!`lQc~+@IW(- zHmyT}ict?@k3#%wA+rJ8jtJxm=961iVqW;72ZVq-j*2os!sOTS3>q83Yj_-Ya`7hd zM(RrV^->eG=ocUG5nI}?!OQbD&jmU%15;x-euwQrW$tLTY(w{r9OlD>s(h*JqE@c` zFWPzs+=zz;U)q&hOHlAAx^fb>aMjeuM%E%;mCxg0?}DnvH+@qHO9XAnilqEm6t;fC zMk-w=Dh}68x*0E&;hXA9ZCE}hxs!S<#B;jP$j}!_t-UShxPvA45{f>Za8-xhR%4kx zGm(eZ#aSf*zNWn&CXtCwE)Bk~AEMss=KNTSP*UKQw7C!gw<4I)VZ~~Jsf3WT*m^@2 za5S3xa!R5>u*wpedr0!9FkiCnz=`<*jC(11zM;LMDTP~5zlKhBm(D${1NQc78P{NZ zk4Op;rxdH@LE!CpE0T3wcz|DGa}uqk-d3ZC&Y2>F44Kz)Ko7s%oJ+Af8BoS^F%GDUkEV z5i^=bPfwyM1lvJ}hxrk~{@#Nbaw#=jxe~T67346-c+CSIEs5nH7*m+qy5icohr#4z za7&AXV{MEWnwDh2=J^y@1cV(NU1fqz;wZuQnAaJ!Mn`OIuyIn|6F5wbag236(i?oN0Vm;wDmv{5X>oWff6X-hD2=71 zYt7{7ltfnDQe6{KZ(hU?t%ouapWfAcBwzJ~+yEmA7*qfcFhuHDuaJf(q1G^W?;9}Z zveJ2Wu#?}v+2kngO2kCLI=8pfjo3GakOF-$oU0SdI$r~~3&@%kiM`1TAOCRA4k+aeDb6HJh1(Og?#0 z_kAeRQ(AX}4C)6`S2y^#ek7&3PD=Eeu6BDKo;2IdrWNy#wvtQ}Sh;QvG95mnqr9ZU zlkPtlfmwn)7oyzPV~~p!GkOBD!~Q$aC7CvtH$52No)6V(%j*ZA9js2g4_W`H3{tqFc4S;-3KO_$s`w zqpMK&Tk7FzX29vnJpbxP`wg}(FWNz6^R~6P)DzAwQep#bGbMtW4JXS*J2?fU=yC4H zHguZT59!a$^%(g+_FnDkunPpb!#xB=hj~UYQ}ug&_}0-loT=P)X|sO`--M=p7)-Kz zsMNFwl(Qfuw!}71Hzq;&*Y|Ip0oy(9X(DXPX+e74i>p@`H$Rj%i|Y^9CbjGtM}xzX zj%st26?0c9XRvRt-P?cOJLi>}1I5Bz;X36`#R{L_tFG1utP42#@5U!Tfm@795C-b%_5nbrHUQ`~TTX_WE{_@QvAgM-(ExjBaX@|RY) zj@Y8c()zuz)-+ZIQ3ScvK7}T9DC;y#y>=2O+E!18d7?8Q*4Gnh0$+M715UhERJ4zo z4g|kPop@;^fS_(Kz7i9nmB#A1Eu_T{UI|xzeT4DScz)`=ExhgO`I!ZX^Dcmm)tOdr z00iDGF(;UZPKR~g=f_xrff@kPi={IxpW$nPqsF%pjoNvDr&^{(@F zJRK1(vZ4bd+r>&?vgoyP)%>&Pxy062!{v1j%DRPZEx27+FM0_LiXAgMvQ9Oz_(0Kb zV;N)%3M=Cco2eKKR;7bNG(j{NMFETvY3)M32?9obt)ba-M=$LhJbjcd{tOMPO{N0cpN**96GlJPDz@(=!$eDxVachXOA!Kng!6|jW~!HxI*}5q6@hn znV%w>g|2F1Z0GzCx@m>p8A-0v1Xc*d9K5{vOsN=o2w*_nrQtDrhzn;WmpT>)XK`wS z_TaRUOT5_uUn$!2F|W($Uq6~vztw{E#L|4s$uVCWP05^HiG}1c%N`=^m zNU{}3E>oE?9$1d;438B%LDS4KAPdzihtLL&jb@_PiI#yoaJrNXfK+iakkohVPN{K* zK7u~Pe<6c7BF+U@s+>2kUrISzdh`5;R0h_mG+^R8pq4~0S7&TDgA*}h4{1(P?iIGY z+RS=r%7L3vH1R5~IH1j`cR-;VXKdnPBskPvY34^m)fE2Sqk5nF^T|2X_n}+5{ibO$u&@0N7Y!E=#RWoGg3zB zH5fR0FXM3+o|DU6_wSS(cOG~h&-uC`9XH4Wz$+dKWcAWmLpjgHMpG*yJo|(u%(sbk z6kH0*96P5;gSMu-Jw9}H_Chy=R9;9zd7@mOpU3J1G94ih6e#mtE!6B)TGBgx8HZ$_ zR30$C^o(3y9r|#Dc2p=a%zP>LzAF#on->E@PZ|rHm$DsHpG?Rp+01;^(&2(;d(J|j zyRgQwn0Y@lB|4)ZZtuoYQ%btPtSP|GJONXsw5wjgo)|}u2b~yg)JbaDhv zsMQ`Sv|F#q5)clL;)IlCti_u2A|WDV6yUuVU_L?hcK9r=$@c;!uRKq%z1zpv>;s#( zdu))C6D%a5g?G?uq<)|?u5L-3e+3+}dF>$(0V8+!2|8G89(3B>sbr&Sd=U=WY=xc~ z*f^!4n*~uiJ_NIFFmT8VqZ3zAuTRUGXba6KJqV9hgCboj;K~yxGUh{6?$~Gf`A|PB zFAJV=AMnUD{ai2ns1$oJ#y{MOvcU%!(!VK>>iVsI8v3mNyu91PUsMEZ+p6M!sWqfe zO@G?mk28g&!guxgkXxv8Wmm*kY?}o&)G|KuWok*04|)xTj4;u9neZ_sd2~i1txj29 zWcNaRsHo1Io?XP9AE``>#B0ufx68Rmr$AG#OT{(`TBOpOa&J@BuiXysW6@Z%j*k@_ z1U8c{{z%jw{JeOuua^Z0$Rwo0#w_q%xqv#N`xKUkhsUn_orwBHs{V24&=eYX5JaZg z{t=ee))l~wq}Z1qC{7|LC2fYKjK@V~?J|eNq@_y?aw`}p{Hnqq>BZXiK)E4Dt5)^M z-ozwf%%0Gin>X|zzaIRazQ!O3Ru~0t*Ey}u*T+*o7`OFLA5^zn1>_yajPpV6kqi0v zUvPP*5Nhm9SEd>-ry<|3$xSTO@364I(l!D~way}YOhlmX$>HYbrg7rmEQ~rk1;A>K zKCLTI!dX)9_!wSZdZvP)%pWNFp6ov*Q|E?V;)3}%4feghT~~>mUyI+HM%h}?vJiuJ zOayese<E(#b_85KJ5WLa~ri z#m;t!ckO`k^Y5porQ^kl)uK1B{i`&5nAFJ9)YxtGysYZT_%4;$*xtEn2~x6lY#uA~ zmYh7RoeWzo{Nr4rH5fj7IkRIjoJCZdp_hjx=nF1Llnp*;%uXY%F+6g#EB=jlnG|?z zw(4bmHJI4=Kkl(EW1Lf{V>>{i?j9W}Y#i!eZZCvGNyL8{+s33)$+8zIy~q$E8zcj` zTiG6k58(oD$}LA@>@<);N^?7ERhZ{a7bGAmmYrJ94G7wes%{5`NiYMR&9BBJme7C)Ky?cp@AQ#OuJ%nJ8()R%C63DZV(#n8$mpc1@D?{ z=&5O`a@`?Zx@brPP`V?A2^UrKS5v)f`z?#;X;t+-c+q;-!aNh#nTwISsd_AWFDKz- zTD%(@TAP? zHk!?_)4m;4h3~4R7HP_5E{0fl<*L66`WCO3nqS`#P_{aN&`LN}&YGF#sjzB@b@wyv z63bB44gxE?_?=3A(|$-;0_yU8&f*T4V-s~ERl_DtK%-3UR7SH&- zw4@1B)~D#Rvu^x4nTw?d1tKu-v!~;Jo?t)KU*>?w@Kl+`w=l~5`ikuF{$>b-{D=z7 z@DcOqS>bV=pd8F@D1qP(*DTkuHO*q~k*jU?qCtIDUsS=Q!D`?afQyg7?CvHABiu|` zf;)h=snk8A<)jG2tmwZ zaO(LC^7u(wlCx?YosKmuWod0^pJzK^rsUju)}vezo?r*U(cJhzT8p7P%-T%bdb-j# zp4kG;qZP^W`;^xEthk&GBAFxaZYGU%sKV8Hq~Lghjl6WIO+#!KD7x2#dn zB_rX6L=@QxruYgafNk2?JHyadmtnlzO+7CD!b-oYn4}-TTs5qKuNbJM4+Sa%aIMaB zaxY?-2vN6P#C#VLG9#{pRh6PHJpt&^2}w7)N=8^hZmSDVlx@;IJN(e1P&dmm)$(`e=$Zd?@R`I;?y69NC>7gf^14 z4<{n_OMDFLmorcs73yoSr9ZLCfFs&xwcXf< z-t6q|S%02HD?^bZ%hMA`Q5Jyu3xqHu7$YvQK;eV^i7Q=g;oyb>2D$BwdfoN;9cz(7 zm=N2*H}&ng@a*d8_uhNH`TNm~ufEbCjV^XwX6Yo9K9uw_@fZV*Z%jN>7GYLWQebKb z+BX?tNnML(j$75#+_2#fZN3JjY48sl!{p2H#YI=15%BqvRenk z*Vei6ZhpV-?CcnymT5Iy{o9QY70?6R>*-ro>U%4xf36s~j)3Ux?tn>crOWp#bbRlM zHDR5jW?&N$GSMpq^+);;<;C=^#omJjI9allI=}(-BGgjz{6<-3F8alA?Hpt`t8cy_{w12j5$5 z*t|==3->aH30yMkT*upFln>7)&l=NVWHu~|f}bf{wWT#vsY7d!2eW2d%pfCh+`slP zSA4SIRGC_sS)SkTg1IFHkR|A;bHa+l#5~GxoY^&HKJ|}Ia^DVHRdxIJmNL+_%wcoi z!QYX|&PiA;Tqjn34n4^K3Y^2k=Cecaf7ekXsd3!bbLrXrijKD0Plnagcq)< z!NCKW=yU~m}QfbY)!q}Nm};7VD?wHyi9tZ`~a=vB9Z zU^{qE-IKiWxaN1)y5MCUORX&;t;4+)aTDijE(8D66yEYALd}csJ!jnqqugOjYQCzHBGTG@yHVIAsJf-{F6>6y#7m#YCx8eS* zx};TZS7qS0HJr-qRp)yzPJiBwsYPFs%g%TWy(_i)E77YlqQ|plxC(l63WsjxFiy%8 z&)wp_kCKe$riXt~G5-!QM(VPm^rLX`) zh)R-BAHG#521{CW5z7i`@9P_B=D8IPvD^LggBcy2p7QYph_HnrS;G;i0pGw$5tbe5HT2UO_y2R}m(+cgiP#gfK|9+CYkUu1%OS~bGZaH`Jbl~e2=CbM%_PHjo^83go@?$Vv7HS zdn}!D=DT5JS#xG3RGGRHRvvOhhU)86c3w?R5;)qtOq3Fr4C1)wlRjKWL%c~eLNI5h zSCP2Z?1^dcx1p9@TnL4(ES{Vccd+(3eH5t%953LgmB<9pTnPnTyW&~Kg>gBNWYVXxtrrh6EMd4JA||PIr@SvGDJ%VgB)ys>WLO|g9=6pT{YSQZqYNj#_ZK50VxRxe#Y`rMZRhE_ zpGuo0M$cZ{SGRmEt(eJbukpHb@_crPV!6roHAEh&A#W4W&*Z4j`k?L7IK-QPY!}-d zFE-H&?DY&PF!J3ZR2AI8X9*i)bjrqLBl%`l!NmQN216G~qDyL-PV%22CdC}toVGL@ zMquIR33{lHV_7y09=(eMdNdO0%33*BKXB)tvku3*)K#vBx3jNYD8H*j_I^?#(D*BF zbH4pC6N(l~8i82)sX$S{74LOZC!AyH$>oKW>m|pH;$mo&pg`rwn*T1LRqXslcZT6a z&m?9QkgOl5_BN2Sbo%t1GQwy-Iil6c&N(NE!jBvD#C9`rIPHcT9)fjK3C*WPe7X6j zJ10!_NvJI4Q;zz5+}+4W&FvFWl7vK8m98 zUaB7CMs^%#LeLz`Iv15U&NI9o&46Qdj6mfzG)pfUj5i)@OX#zW42*O*w*lrRq=z&B z@wOSjOc)cCX>(d`HB~4KIgQNM!~@xA)mV(Tp^u-xjo`;9bINW;M&ks|f^nyJ-uNbE z?;89<<47W^MA|xqle%uQinyYlUJ*k4VD8~L+ay5cbKnv+;FD8Ke79Xz!tZdrX}1Eh zd8Gu9Vv*6motc>|rY0;b&pS-B77lFlmLTp;w}!39U?2N}v&0h1?Qg^Zj$5iR&GO40 z=7s1gzUR(m(Lhs(&n74|gYRzp%*2jaVWn&kWHd;S$L3IU9}B;={`F>ZH`aNJXmtRi zcABEZmu~{g?Qn%C4DFCZtaH{<^J3q&1SMP}y^paoeRptNb8TmNcXc}+9%(uNw`R-Z z49Ucs1vOC?@r~izVpeJnTST8~3F<@2hkpF%9%1)m_z4ROR`)19Ut6<@8dXD48bQRe zQ#wJ)7bQS%DfvH)H z(HvFIWJ(HcXf=+aZnj~vmx8C69U*;KgVIfxmEQLcNszo?$u0Bisypg&gvl&Ex=$y- zN~%PDjs!_M{S!@tKy_mb|I!~H=%)niVJLqZ2<+JKm&VQdw=2-> z8hImEpcu74+YE|BF&N*6hK#i=N$+%rFiot?RQF45v)VhHGXCSGw*~|2 z+Me2hDh-dc>M*8&I>VOZ$2_!ybNZIbTJb8|AT2V|DoPy}?{ofkYIcX=3;M0o}zq z0o|^e8Tw?;yDZ-l9y#1^>Z*2|)x>+Q;ys^Yx>bP&#_n|+T@lct{M2#5F*+H9^;JMA*^gYf`X98V8^AV)ZMnFmpqlq zqng>VV@{!AFf?Lt-t7Rq854mluV3H#Rv;d*M@+doqvWOd-Z?TJj0@SOx^Ku511H?DY< zK^;gpO@ob;u?V$(Rtt@O*q{;n!ACJzilWSY<8Qvg#jj0*@Afdf)G$?`;Gs*1t|e|k zdiz;Y?kfldm!D`#F3^Dvd(tv={_u}F& z;lg0dW+{YQKELKab5h$jcvFgHS72w=YOT7}Y+z6rnxs*ODVJDpMT48W0w9`VcFlmx zWB8vMHnU=_hq=tKx51d&AIT)yk&>2?)D!6ySk%~G#ashpD##*q)v+-*9~s$bBY(mW zO@x1IzrOaU(`{@vsTr`%LY)X3K~Di`Vs?D^Bx5Ha1_!>#=_v8EME03L&2rnaV%E5@ zlKz@pY@r{ZhG$NhCqywR!cIen1sN?Kl)$L_6A(*u|a5V3G&BYu3ff%k;iy5S$tnUurz zdVz+h?Gn`VoZ8kCevA$SPlHKEB^oBc%RAv~1*aY;SDu;3ObqLGJ zDT(xjMPGl+J#5hWv-&0&5lfQrA{D@2m-@o1det>`nTV`Es9k+(c=j*aXmB5rca;hf zaBT@xDqV66Gj9l|4rpr&)^FP;pQ=ugbUqZD!zzP$n2{_twf!|f5};vWh`KISmP^k| z-_p0;-{O1VlU8L(s>RiPmjr}|zk>Vy^6t&>?HQNu-3(2t&szR%+kW2&lE7;6FOCA0 z-FKd@wYIyx(`Q%=G&A;y3N!tykpbbAQ(kKMIlvpq2lE&j(xq~lWR zbT!RIIXP=F(}3B{>rm^Imxm13ltoDUU=BlKe6D@uA5lTKfaIJdHj{c}%Y5kQNl-80Yhph-e6 zljh*yyO8c20ZuL!+>(&jfDt|@$yf`)q02YpRB>OBCv|kNjT*}86ZV546i$qgVT2-C ztqTdY-wpUHO@dVqlXy~UVamobJ{|L9U1b>(f9=i;WUak5U=8!0s+2}0Gx+_|LQa;y zo;f=-Ssz32plVFmbBFddrB@A%+Oe67F3mpKVyh1>n&y2p$-DxhS(5H_cFt$EH>Q&I z^RO&J-b1DeD)GU~n@chVOjfd|!!FkaL4|pNyE=r)4f|6PLHU@D^Qls)x!}LvL~5#Z zxmJYNfNAaQg2($p0;x#Ondt*OJb-m+?^`I?QfvJwAGuR=+&U}{)5Ms&h{yjStf}u5 zZ?nMT?9^S4!G-sAq;el_sd4m>imT-(Skn=Y*&%v36eC`a0iI-#xqR>^5qdix3`C6x zTRLx8#7OVV9o-_0rok97LQW63^4xY?@Wh$F`7NQtubvh|;tK>Ka=>P^)LXL`Uv0mC zs3wiGm1IllOpLioGcC2km1Im{`b-vT4thjnE^xD!b)u&5#` zO~ikwV{_}}Y1VlyW2XIHz9HTWep-1Y?0!j&*Ds3rw3}znf2={vqrK+Q0D|Fq;!c^v z+RtD|OfT5VV8H03Px0uGt^DNubhAwBTF2LMt5LM13dXP&aW1b;S`%0VV z-g}~Dr$vq2$4Y8Uh3wE0i_t+>2%Jil4_sfGrR*Qe+=EwYv4Wbb#p~9ZcrK_Lr1j16 zagC9++_31>@wmd`mA!$)$a;o(F{k26(cD>p@SXbmq(;t;h{IeJ3r}F<^_VcMle)gt zKPBX>IqU|(r8AtC5zIpCo22JY2E3`GR{fun0X4Tu&)*C#*qYhTDm~Qjy626a^au_NrO@s2?2S4EsT2 z5hF*2uA#yXzcOX2c`J(kbNd9ccM}NEXe1oxmMd`J7{Gh+yQ(nMr*(d2WlzgDy-4o8k@e@YrwHZI5 zzuaq1z^QXh>NPezpx-+*Jb@ilU)M_K@Q(OoYkmb#8U6Z6Gyg0ff!y{6_-1vHbAEYf z-qa_Lo_FB^!Tvx|_>r~0qMtkboPj0QF8U?PD607TJZSk{gxwwq;U_zMUa$TF$x8tN zBLn;cm;EEY2>`5jqT1+A0{}>F0swIQ9}GAHR|6VD1E>Gt-=e*MqG_emOgC9=H(PA} z^_~9?cK!`tY&KnAeLH_ixqhWx8B;jRD~`&MXPsn8N|8wUCvec0`%|DxQOUz!@2+&<`a z6u`ajC4Vb*eu>+f}8pyNL-C3R%3t`QUSQK zi=ClTGm?idFqzX(P1&$bv417JmjFz5aB8ZU( z8Ca(sG6D3!GbAjbP4p)aO7+}J5XeFMi~m$o?xjYD{{q%_$Tby5T{$1^`omngS+-YU@IEdyztNIUKtiroCKd`|Ij+nKmM@f|4;xv4-29bd23Ol@ zIi`7dOgk^VY$@9+h$p7Q&kaP-n z1s)w6cB%pFdtA;)xzdbH6ZFq^Xi3cl$vm*4f|GMH;_3f7luXYLDUZvvf=g0GVG3Py zWV@>4jEr$L*JW$$LepER{-Ffxrb9O;AQ z|9-J3w%TzL+)0e4VcogWW`=9+m#yb7HYmGPk%AJ_#;f&Msf7rtew zWBWQOp6iTOMP=i}Rp6IVBr4;C=hkXtMwVVzLfs7Y_vo>-)4Ud2+=9bkM00u3eHk(g zddhFRbyl^oOtX>gt5fS73&KKrC}*@dMn~PUl-E>sbNRJ1tnIBu+lSx*&JFe_Gq*BX zNl4PYK+-y+Q07@(wySmfiIszDk1{CaTm&{JJW^1&f2CX|?A-0UfDjzYCxw>;=n2WD z+Py*LgVCs5p+>&;kvbBS{Zd;euaE)h98;!%gt#-!TMQ;7t(=3bMKf#Ir#Tv``?59} z*h0-DRHn=*L)9F=du6b4OrlX~X?5Q=n2STQ)4r~INPKs|LdDdiQbD*7$@Hlm>{|)P z=wZkzZ9Vt61LD(r3+rmJ01O-YO^8HDDiXyXy#Qy_gWl;tRH|2<^pM7n7}!sbJv&@j zPzT>9ZJ!pBuaU@-s)Y;bm%Ut_o^Ss)u_s3vHQP3r z%zl&asOS?nt0HrDJ4Y7qnNp%L+x1uhM0oj#Hx|=^hVj$(n)KtWN}(fR=Br6lgZOy= zh2qv(#ZuYzXQ#R6X5ne4iX?j{%TcF{oAec`dK1ZN*;_h4p5dYCOkh}m_~$fw8`O(t zF-y%%N;&tNZp6Xp*y)+q`DHuDr_?|S62{N=U&0*i@aQqbjL5sS2&)48UO+2S z<2!j`3=mmdi_VkmfryTq44U>rs)>{9fFG#W?XDjxh1>K_Kb`+pU|)rX8!p`f3Koeq ze=84cpQE>|hKP0G(mSp=^R!)p0(^v9BJOIj7c@-xh}Vz-AAX09+Pn|!Tq;PG@(K|% zE4%zyCi?ouT+wXZ_FwVVX2t;$KA{0a888`jwUb1L%3xaRUZo(_5w)A)%%ZoD!kfzA zhZ2p`m$ww1lQ_C0NMKq|>Eo*p=+%$(o!&{PEq*Zt7Adg0LKuzNloxPG_zzEEciy{q zS^_*Qd^s8TQ?u&P3(@*$WNf4!!jvWoYNu$gn1LQc8`{GO)%{>_NZ@>$6Y#C8RIY2W zi03dHWgNm?qsnSdTVXh5-0FJjbDB|iAwW@=F~SnX2sTaoC>Dg9Bd06HqU|?{zo@sQ z@3l!%PBc7*-7|Z5QvxGG;I{mD{)UT511v{ZZ7Aw;ly-vHD%w7_YWvXQ z`1yQg{QQ!?Mz%^0_D-w}N(@flVSQ8ovb?ine%Qhy*t5R#6xXN5r>BOW^^#Yr3l1No z{(Nh3(U`4&C^9}+lD|-Ljx?!u2k@Zs2G}YyJv*u~BgmBm1dEXqol4S$#+BZ45(S`A z3WFVIGV=c{;GiqiLrpR9Iqtf=ENBX8o&QN)UFqlNy$vmO zPPO_h7~B0l1|oOu#k}^IMLQWR$6wPjaFnywSs~SlB$v0+VRQHZr)lNWYGrcZk4YoF zB)(sUKMsgQQxFN}UHXwXNlR3=D37Io`nT8oZMu_miL#V2&7^jc8kP{0+1c&*`Gg}^ zI;ywG2gxr;o%Q_^*m5*TJNpdz8n+j)|8Y&akeAAw&^fHZJsUaLA#7a`*kIfy8tHRj zLyu2dBrA*SLH-eclzc&G{PkbN@*+n5u6u~FBKmzTJDZm_q9N0W`z0D9J2j|Z-+T!N=~efVxxfbTZ<+6zwVLagBMa=oBV7{VEK?)#A^OYNlKvU$ zoy7`{*svGKaSLZywzFYguKSnOGJ>~ezs+F6)^6*lQ_@(w@^Ra4r3FZWwzWs+ja*Uq z;4+rZzI8drxkpuhYX$m*`Pq%jk%quLTgmR1e(W1i=~74uW+#@z4g(gTX#YIMMma(2o{0@Kvl9uhoJZhO5aw?p2= zqEtM`qKh|kgAb+&g>{@r-$QQ0^w?ks;fpXpY64--iZ)^yMdcEzGW(X7i0-rIOn>Gl zslv;5!lehUBATApEi`b8|2of&-t_65CrijVqg+Ej3>o^^F`K9(Ol&ssbifl;f*6{d4|e&9@-=lINr zXK+N}kp?=WywlJ0*)uW>d7|T^Nz!%%b7WGl0M>xa+`HSW)}HjXx7HXQSv!oY0`9gg z+6BB9_|Gzob~qdwj4U(O<@`lk2Ha?Rp@y0v!CwqAol*>h?_a2S6Q&W%oX@DMIH# zWqTg}dO)4?qNcX}6ZdJplHtKk-=W*rolX8hYh{H78L&ycq#GR7O=)|@t>-(eKnl?5 z1U}0Z;{j=pbmS*w%cHC0xma^WLwRhZutSt(VK)xLM&A0pp&eIc+NQT*Tcd1n^=`yw zd0!;znbEB*CU(bYnNvAjQJS^Foi|+L3>9iS_6_TxVddB6K&6^#kK;0PI=a(6dSu+c zw`D`E`jvP;M%GN?J9c*rFt}UIEbBC9f=+rnwuGe1EhTiEt;dTGEDDgH)3;Zd0!+0^ z;cR_^L!++jEPt-u4z3t8>3*amK8-MT=%rVwu^ zAV%?l{cc5%I==WrB=}UU5AYUQvQg@$C2HMgd}?h<1hRx?rHbHQYmzQ$jfKMV1&1|Kq^;e2!v4(yj z4Q84(CcagE&hALam9BXbrF8}H7}Q8PyztRVeg9a| zL!v99E=Y}fmqH7T&O#=|y*Z#C2FZqAl<%Foq;InCSV^hqydn2&RVP6<)KcRzOv~D2 zZb2Z#gq&hu^iDFk9O(Yb3{Oa(lS9^=y+HxOarMylV9KCw*qAT8?Ms4?5Bk0DMu-=| z=uT7}#>mAG4h#tyRu=hgjJJLi7Hpyz=EM%4=oYrTRcxHl?OyAF8Xk}{5V9%M`a&90 z{9wZyT%D(Q+fnW`HA#EB+4 zs>@HeTrWL`a_0D?vTFme;!s*pJWx;}NQ6j<>0n5rPKmOHgyTaQ&`~QLHy_$gJFWKC z=y3GSE!KJ!E$*!!0J|&eEBlWemz^vQ2lKzOAllpu15PoGm`>jL#9;^XZuK!KMMe;@mkRm1gd$YRLi?T#z&wM*-cQG<6MIqts8 zH&6ioVGzpck3in;EU>`q@FH)-?CTKbaAU_S%8O5|#rLEiB?iOoCY)Z^m|f2~Dnmq? z+g+qa7j@4FS*_Qe?Og}r44Q-I(CY7f+$-K`ddxPuWjh%--99gZ;FQSM-Bh_N4n*dcUjS!F>*V-znJeA%`zU z_0QbUQ$+pKlLQA;|E1x)uG{R_Muo0XRXQ~PyU3SegN*#%y8}FiE~4)l^q;w5f#0{0 zuW1UOI=;K8KSq163^Bz0Bnbghdr8XHvX4CYQWYzt4^}uO*9pazlS5e@qi&eUA19D>EigoVF~*(`;c!imNVrUAJ;s}SGU}(k8U=Z7kc>9E zx_cdh4QR<9o=fp_X~`2|j7jNpso|GQ7D<(wl}q4@*d4Lz$-hi6a|PRm)4oi=Xq_df zMSsZZsV^3fX?>S^V`D!)PyE$XKnV5;8xmXxSt!>WXTaSwHWlmMw$Y-?plQT5jeDt(45 zVhYKi0mbWlfgsLl-pl2$utszXH~z?aH~IZluT~&gM}iT1NrB`O)V+mRQ0H!^clCZ{ z$5wsO%CTZwCUVPh33Cap>H+EPq1rk^T<{l91}RJ_&yscycN6XO8qwR7u6a{+Rk^pL zrv*^mQ||PUnF@%R#&3@#tl84sshfay%GPSAsA;cpQqwchscdv^(puNlXsw_x*SM&H=&md7bhMb0J*}%QDjO`WwR8hYs{P#L>~v^|Y*DrdVBIuOgCjPAR_0>= z3RVfAh;nkbyIXO4q$sFXYwe>V+kQ3DfpCdWaFfnZPL~-_V=eNZvZXm^8mij7JLztJ3*g4l!?St%G+S15VN% zczy0xjt8y%y`bno*#ftB*q|FGFyLfvD?Dcnjo-+&gll_wV}&Tb0dlo1lv?E|k?*5I zh_dfcsTg$yhitUS0^b&k)ya$mp9zXgB!(MQpR+e}EJ!+sN-A3BA|)db;iBNELWx71 z&(__&m%ad7fm=duOF%SzsJ2=u00!`sU=)Alm=k@5bO+;D_*A|qF=KHq*;;2ogQX%r z0e^q*unHS3y=&t_Q3quSe&l}uHL?YmhY%Fmq3E?TU|hyfyXj>@^WZkB_16gcd)VrE zJE$KBT9kgt9okPspM?LS#6a-yxP*(hd<5q~!rqPTiz4X~TZ1)IQ)$>JC0gs;{l z?BLP9YKmn#$B9-Pb;3(6huc_QK?1Xs6Q5{=Hl{A%%}NnD6I8J`Sw^ys10(*p=m_RB zje#!BT6APCw<)49YNc3`a)loVXnGeU#W*J}iB+fYLC8{S*8~MDt_snUE#a9qtkELG zQe!LoTq6PC!ng)*5&kh83F{(&uuFLlYJ)iGEF#!JYsd;Q46hbs3CXNjxT3}{1$MiS zkNx9nD@Xp4v!?Z-0f`8u>bDwQIh-L@>jfYPWFfMdTkv*zr!Ig^q1=ibA;S7^P#Ym9>;tDck-s zifGDE%GlCjHofL*X9lpWiQz2D_7Hb)|FV*Ew2U4P70qo9+!szkP&GA`(KZ?IisTYb zECj&0jl@gUNyusjnYQX|qg*36k8PjX6P?omMMCQc@ZTnPhI+rtN}xL=TJA{B3k-z( z!G7a-a=-DYY)PgTd0JAV}fo=hKQd1mc*}B)suaXYlew?8NKPt7a}{ z0@XYDVz4W#AMYl_46MmUtMIdDSh<%*Xst`7PKBnppHKzgWiYzQx`Qmrq6nq~W~4wC zs~V5s7U*QMC4aFaaygBWnHa0#jL1(XT%fN@I>xCPNf(K4h|zqAZ0E%E8(P#YM$}O2 zqhG>4yJyxr6jqQg)Uz~=3Q0M2(n-fEu>e?T7Ij<-)Bd!fSxAumrhwQ?>6cfM#UU}# zn675g-Yfu^YRm2=5=ltmBpOHL)aPEO8X9m4>rJS1imHNhw04)Qr#5E&j)+xTAJuNH z=Ro5j`kVb8+km?TC%TG<_Z#cMD|1^Q4A_~8D8|I4mvWsYW2iwCJ5vToz0qdsO}n3EuS1QV7`I!% z*{M2g_rx0J!9cpTXYw}VVwAX;x4*@ZIKi|l3W7MQQsYqZH@ZI8Lp@nxnLWf?`6`jr z!iv&Ll-$QVW$3gMrtBZcSTGQCRxMADCf3T*MnvCv+Q_O~Ox+Z6o-MqrLY-}IqmawpHI*S|YJ3cIDAMTNU!G)Hc z#noI*SzgIkV%mWT@8!4|`OcjUDte?! z$b<#EXIPxj%4LLfk;wWu$2+s6dXd_&slq*LH@xQdA#jx}l36{`P#zNB8a^9w7%;Ef zTD{iPE^pYFD2V`(*q#1cx`c6Q6Fu4%%H!FC=0U{eZ~?u)j~#=h^?iQ-f=&sIs}c4})QY_Eus)S-lq2`L^nB^heZ z!+{pC3(HMz3EuWfJ-XJ2~ z+(-~#hGkrzQY*Cdc`EWhM$=L_oCcX| z%0!-Wg)*oe_Cxx1owV(+%Y^PFdR@|wwo6qJ@iIaK@hH=1*mt+~(RiODe!Xi8{v7kM z#UlzhudvTz`dQXPtweHrgqh_dt}tGWT#K=XBYqKmLI&1DvBWAZts02Ly0$h+{ed<8 zipfbhulBA&M#Nl(M2+)%Y7u?9TBgHhIOpRzW4yxS4q`7p(4PMIyhM)k`v4+`M4lvg zp_z~LP_O%AVy`G7hm=A!*@CFAP<8(eE(1!HHLj=fAS8btFD$cc%@3y;hdGR9}eCli||kQPN+!L z5cT#ozN8Zoq(Q=a{R#A}yefcu15;Q75pCfO=pI8q7o)#evrSMr;WpPgX9 zzUO)~V~&23($P>E=-&P)b=9^Zz$-9xmDb>74$a<|74Z|x72iuEqsG?8x%Ya58)(`> z5W~6$eg598u-x^$E_@P(w|CY#&jB5Ps6ifki#sPN5D zY>{U$C$(4Y5;FdD#C-bB^p#7i#l44ra})6s=`kPUII@9C5s}h|f0KKoLn(H^9KWe^ z_0ve(VL5yu8*VLWr;Uu4?olK4@6+>_)Kc~_-u@Ht{U^#_)=A6fJ`r8*i?lJqRZE=Z zP~m76X~bNcoFqViA+dLN&oAS8cR}L=>cRkhytVy{C+xHNGlt&ZX3XR02JX%04N~uW zKHfjkpZ(D1ezxN?ifAXzpY3qxaL4TpQRF+x=9}y%I$xtT+?(nz%VF;kWY}lir$a%F zP(oO7N+u&~xm8$d=@F)tw^86R)dI{|wY5HI?cjymv;-j=Xo0mP>}VK!^sdcd^T2>_ zb(tit5V__nC0>ab9JRP<=0gK z?+%L9zOgGz-gb_0j=9=!;Pv$@$0~$zDsPgl0`#!?xi<7!IeRQz9164uZfy)p6ZD4Y zKg>=*hz3bO_R4}B=25|=&Rup{#2#T`%nXkSJ3QupXrt0*NG-Rh^y%DGkYfGD$}xJn z!77qflaj%d`hk#-H%5A>oYfDQb0mihQ=gHojMqUX?;tC8_qMi7A!XMDs39A(L^WCG z7HgrMSvJDk#fq0J>DaToTZVtHkNHWg9%FC#+6C6ou{Mcm_Y~k&(y=#@Z77TI$WJOD zNG+BuG~gqP=m6+eo6*D6i>jBJwe@sD8R;LiQQy!vt5tEeb6T`lSFkp;QyCqEfyTtF zp2Aw>brgnE8v*-|o=eYZPBT@J?pes$1#FhMoFhFk>6@BE?{5Jsrk2wicVR2U0)muv z)8#eIING>k{NNknBAdl_0O=^<0Wlt-O<|D66yR)0xfbM5_j3b03iHx-2t~P`KTtdo zV!Nhxoi#bemcVods~cKf+zV7=+2Xj`rQ&Z9Tj+%B9#%YwbYdt^(lwf|8^ryFpFlA+ zSFBC(Et7NZHc@WXn{(bx`ucoq8VzuCRZm3Ow@x`bOpGC&aF+x5ft$Capd1I#5bpOD zbT!O%=$mj?sxh<(1t^Q}R0epo-*#}9yHn)IKip}YkXJbvd=%v&EfTBGI2-(OtIb~! z^1=-`MY<}JkF12j+R8AwtMx2v^;xt-xrU@m=;w7aX>cU7>kblZEAN%q_3Jz)rZb7F zgsDx6}w?WET!|VJ;js^+0KlIM^8WqM>o?tl=+vR z%eGf0?#VXN6bB{*vb-z>Gu{yLeSc*RAZLbUUmenGg96EZqQmO2&FSHe_L-A=6Awir z&%9GDsgl9~z#3+_c>}+PC;b_AwLOl@RY2(=?uruKP!(*NjL&04bXG2zmK%*JpI&Q0 z_>1q=n$y7u`~{rnT?=j@EjXML(F$v2C~jI3CUs1s1m2L%kQdO77wVXnp^(_8ay*C2 zhPb34Z6T-ArEZnzgLch3Dr0+kjxfXy_P+JZm^0_BiDia1OS-6=dSUEzD~X-g)0vQh z*g!EiI+hb!x3w$@XQubtOuq6Z*pQWqN67Tn1MJ|h*3Evrc`%-SI)VY4 zVW)0oWli@0zT8%}S%-WZ-KNZ7U3sy=%tudT8^TSj8Bkqa;&(LC!VMGZ64*k@cQ?aL z>E+VNUx0MBs;WE69c1De>}03}VTSd%L1N|Y&)O51>w5*rZ8Zs+C@FOaHqD&vpZ%ek z9A@5Ne$B8!BseyPvb|`~opEe1b`}0S!1aKOPIc2Q-?1j?dpQ_PvR;koV^4f|voW-GatgmI0ScT|ZMqVBg z*O6Aw@x?v}{F$$DTamG)ZwV}R=B+HB4D-{Sn-xmW5|yhNMF1gKF9@Hyp9u6wa+@C` zi<1I2HK22*Cm_Gx2IFkRS=p&%W|Ou)+pNr~%t^yZIvfgE3Pvy~G%eX*u9BU&4U;Nf z0c3?XMNG`2y|3$&#g-10-ui}^xNv4BM2yGSOMe@=ZrE6vo=8i-9;daNGY1>WFoicv zTUjvaE_BgyRHpM_5?bO>3(lC@IP)hKGrKk*;e8?ByECDbU)#$I>n@B>NuF(lCf`-E z+~t7fP08E#a%*SwFPQ&wg^H$>!;igPe$zl+GB@?&mSEYboU-8T&Y9pcmkUmME~>ht zmB53|N;&If!5%3wDoQ|bJ2fx2!AW9yA3+FDP2?$_eebu=m6IPLz_w45lMg}0)|Nf# zV=KKdYZlQjkpUTyard1g`$na0)@f;7VP{jZ?W8VhIz-6e^wfL)Lm2$VnON@(3y`+R zDt-0_`Pd9@UWuIKvWoN@iprt#xD1Q0@*F48>{S`KfYk|~bMs7|eG%m$Cb9{f+f-iN zSgMQgUkkHTOVj2qsw6Rzp4A!eIfn9t!kU_8l9K#d&%BF3eVShyTDZ~#kZ5V){@KvH zb?97UGsjBW?R$3Jb4SR^q#;C;SZvg74%jv4Sj$@t>+B4UXhwbcnoq6^?t){A(5$29 zXlrL>R#Kp_)LmN6PN&%8^%1Gh`5Z*6nXJ@eA@MPM&cZCIseqRm z=HiC*&ThrL0KwzQ=!zP*IPt~{7~{T?OzI?QCrXIW9{E5CNpy|7vUIfa?cPWZG}$uk zbKAaQ4vCOb1sIr=<1VD6+iL39q>@5z_x?C`2^89tV3SQ(20{MCb~o^r9?dP8JKt-t zPHyhTW~z>Pa(zHgY^rbAR)2D`Ty@YF5i7%on{UFcEdSVF&m@ViQ(^r!`=zpJZVE+Z zr;8Uz`C_p_MSE~iMLi+IM|SoXTgct{0=%7;xLvZJ{Y zt8XXr8)e17ZI`q^CBHe4eQN4Nt43|Ju%StJsgvW^x|~V*duY4p6n*5Vv^V&`)M5Fr zPs{gf#8#M8=XJ-rrpZN%+Om~Ycs2v)qzGXe25YFYP!N3-=XFh2*XY?-Vod&%rlb8} z(2W!*3qaQ2pV=4b(p0XuD?cHg&3a-PDh+}N;&!JcNB!9&gY1YUH;G`X_gtyM?U$Gu zVau6}SuU2E50V}x-m#jQp{ih_Us+TyQmb{`&0OOfMqjl1oXwT^46l;y$Fb_< za;7D^-_nSvxYShBmD2XRkML2y7;biZ@q(WrK&&BRgcMFE7$&nS>Sb*VLs$c4Q0=2&yJOHFg+GLj^iW!)LbTnbl<{QEARYx zZvEItnWf)}W`#d>(sEZ<{fTwl`L+%xIId|{1ym7kR^lRAJ9iUj4~JXoSsS^WA+g7z z07@KspJXsYXG;9c{z#BQuoocLhDijKKBF$;4tB?UZGReITILl}a@1$1Tcuo8#Mwb| zR`!o!A>Q8jiyl~PaHh^ug7`YGIWuw)i}LG+?Dojz{SM=zR!N1C;2vnguv_z$0VumkROgHLO*^~ zXNAMzbyLpb*GG+$hN2uo3lJSwdbhfSt74JT$TYnGa!@Ug+A@!Dlj7M9F%|gAC3RS- zwlCV|MQR)G%D(OjFakA0ohhOZOQVxHI5@ z`^w!@VAG1bf#*AYtoE51$gnqgCb8}OmyqL@%6Fqr;tm#qKPZpow(-*wiGcam&$q9y z^A2C6Qw7&(_sTOoF#JvmG_MaB6mg$Oq%(>pcbEbDmE3P}?s{bf_m%Inr(nA%Cf}Bv zNT%Q~-`;lrIfUC!u1OyKN4l@VzRc>i$e06ExQ9#Tdxc6r9ndk}ZgZeMF{`$9%aq>; zZ#jtr+ApmVF4N~8mIa%2^ro;Sr3n;`qH({irJBR8Jgh}Kx~XdQjt-gxn~EUgG|=h@ zgX3fQ4tD`74S8D8^yKawcJ%vvFr@H6MCrM>;YWTHAh6iNoB7xqrbj;ZJ9xx7kBEa0 zxOe=MSN@!yQJ!~&{F{i_ci;Eikfbx8$cj7|zn*V}8^7-o;3p3p`O;psB>xF% zd0|iDcPyO%SVPyo)7-?@n?k@hr{@F>L3oe^S`&5J;+nvahHP74@Q$!eebsf?_Q7_JJYc&BkSrT^=1m76 zx{_0xaAs{#an0!o;cY;XrzTAF^-#ZIcq2!3CJ8{N1=RWB$v_$92O1gOL6O;V|70=( zp@O5!S~tOvf0VwK9;-~!H59r;-OI$HklTx8^(w& ziQ7C!(m?KFId`Ln2Z9*_0bnKTHf0!r`DK`&ii1Ok*&&88AU<25G{_IFfFmY|*^KFG z8bseAm$XWIE3L|y@vR{ugpQ0TPhMbvrxK(uemm)-@2Q18vL*H0%iKfefO|B|BUV+lFLG(zcu8SS%S#MBSx_EaJ2Uiq~w?#;HcXC3-zts88E9#L73 zze=^QPJ=!VHKD2-2S0S*F1iI_8{%uLj7mz2H4Qds>&DBJ4oy&CgooS{_Px9)piZrF zp>t|L)1npKmI$$+c`%7M@QWgcl*ff^Nalnrh5Qt+=Ju1_=pJu>)dM}|h+HshgnG+z zUejz*(`=!vjIpXCMmBWdcluBa!j@bY$9(U!XHe)%&X8nk->1hCI+}*@BHVBpH24XFKjCCOKG5P!~m1#@F(dbIDib(Tk%$ za;rwnaUtJ0(54Y9Fe>38!2yV4b6=3;i#sNaI|a9kwjh9YFj*DLO_*MBqQTt`TT^}V zd^mPv=X27nBH zk3LEE4PWlw66Z!#*c>)N!k_X;@!o~_Yr0YqriU}{+rFsRJ5>2vUd`X z_iZi+B@);j)-k$R0A|~|xNsto*rgbmjAWlA=Fi~eIEc#-&?9;O(!TAjM(V3v`Y_qB zQ~E!7PiTENqS46IY+Mp`t}Ic>Nw8=?;C%}?CA#5N_3|)D*ehAMJ!svD`ipHjM1~|t zm1IIqRBh@k;l_AXs=h=%!G+MJx1=)9b{(Y3+*#UqW>>&wu06Y z$R`~AQ0-p*D@$b5-(j*>etPtxJ5HsD$su7~qEwR@%l;H&8ZO_JYJv=ELkWtoiX=L%%3y2&wubzbLOW7QWGLNtg{r-1CX*kvP@H68|ig zRsAVrzjdX?4TRj~`UFdPyR-KMnayu7{=~id9wpz9+pG7y$kSle`Jk;1Wn)I7NT#Uri;td5*p1UKLA zF6n{d6yF)w3h}oVg8n2yVUqDa0<3kA>E6@l@a}o+Kt-gJn5KzmRvsLujOC*tVP&k8 zAWT8C#@yvRUT6x;rtW0P4JER$(yK?1)-0D*ntfQ zQ$^MZi5@#ZW?ffrq(vS#U{Y-x-OBCJt7LtgIbC;l<-Ve=S(de=?#SBxV%4_haOhB* z{FQqKv~2b%n`H~=FbAQ(6k=C=ZwefcAO*A_N^a%%dNuv5*}dc5zGHmFek9hrMG%DKgeN$l7?Q zO?Qd-wT4L@D@l2Gi`I%q#bQMH5PFZAAF6F2mG?cZT81_6+trVgh z21Y1~>6uzmff41^SYl#g`ue>jC1#ozv=7$Fjp~<#6sOnR%L&$ zhdKq(p!JEG9C_(uO~6av6#Dea1sR;yl0dSuzyV9U-H$c%J0DP|U#|h7wvFf@J`yJPskJ3 z0p1_1eh~43ei#B4fm^acL!b(DFFPDSFG<690*pY23kEC&w*rE|QzB>WpkdI1{3tIf zMlkKiL$>n6vm{Q55MD!e@d}7m@p!Cb@;IcT!I#e1qPNO*Y-22}P!uT_6$k?spLt{~ z_wAko1=9Qq=7)QpQf6Ewb#X(}(c!O>f%#F_R~XsO}`7dq5JWI9NyQ`fZVQDw7-WVnX$K~A&9$;lmz zIkTnAc^obK%MVW#DgS^B{p4ge+qu+@D`*)3QclI&JP}5 zAcX}+1-zhAm3{}8fV{HT6u9qyyz0zlMyIr9Z*j8OS(fYx|N47EUK#TTnM3rqEiVxK z(d@v0cw)*4;s7k@E%5*_Ws5-XgbgGEpY9#Vp&GAj2}5O}#a;@qXNuQ7n_rhXJF&kj3fwbxjo@toh7{jQUwMi!QaTIHZ1zce75GG&b z!X-yFy&382L&ceUPz=qu2=olfR6u1MNt0yB9bLjkU6PrX8h17ry#%G><`LKBb(bPy zznUE~%Bl^QdUD2E$C5Q+3Cxdxd?EWA$RZ-a>CHyh&7cFBFfo2)*dhNsNy=h4f=ExO zvW0g%t(iXU+L(r}<@UrnXp-uhjbEHkaGvj&E#YYrgxv5!@9YQ*f36U{_6xlhG^hzQ z&?SW$UkJTAZ3Iml7=m;mM(x%f!h9baK3O++fFT*|vtsh}#_lMBeEsvJ$-D9azbUoPcd>`~MG**AhDFb1JKj?(90NrHD2=}_s zBOxyhci_Aee)dokjgfVb+*V1$_wa5VAcEdX=tE!y2so7r+>@*mOc8zGg0vzC6l1?J z$%g0kQNL0RQwxMw-^y^J|0UnNMR@vv?*i#9sr5kDXwCR+fPdzBw(T}y@aZw3^SQ^R zX%{d0MC%ptV~jI2Fbc~D>xS;FY&g`Qg*~tT9l0JkpqyEKv-iUCnk-N%|H;7oiY89H zUfLBZP&d>)GBF>P^07m*@)KIr_@WHldk(}qwE=$6N^swUz4uD;Sr2|qWNlKDEC7)U z8h_*eW8_lr2=qM>cH*GWw}UGs|1GrtsL=TLhXG&Sh4Qv>D48Ka&N~-y44#>ZId0yQ zY_!Zf^G*E;{hbVvCn&TqdwUYA=F)hGsXDOI1QhbJ!>BcCfP(86>Y(E}+xR%4gP)eG ziqCs6N6pY9LVI+2wEBt*J8+@?*pPer#UX6sWwzXS+fVe0Sn?3Q+k2FfVoleyj}+wX zpnB7$dEkv!%$_pEdW9tY@fRwQEQx22`{vJ4L6Pcc(edqXrDQTk2r?{UbwicJbY zpg=&8kU&68|9u?tzhaR8^B2k#)lGR^CZwFuc%V>cE2y*<#nVtQm`x0C_!6-QYIN{O z$>i`JE?f_$?JAPZh#`xoa%F;d;IFa@lUgx)T~!?1E+&Q8!+_m zRwT!D6xKYNoFbQy-{mWV6ndoo^nAk#c@eZcj{Pw57ZCIaf^CdRLPb2n3ih} zeyg=QZ3Eeq(sdm}R5On&lB8+4A%*E8(*bog4d=>5vgY$Esj72AX!bs<6PS#;?_!7Q z32c%w;Uh$+WDua3g}C~OZFQ&5)c)!84~Db%Q1ywXI1=REr2A?>p?zdic)s%%HM7#D zZawF#Nv7C&Neu-NQy7C1)T|dpzYhNK_T|OS>k6#tkE03lZh_UZ)7U z@Hps`-%1IktcwS;XCZD+{aoM2BG$7|eyIvh-Jkd2vgP=t*(Zgl8g1#eh}gToq9oiA znr{pF)M)n`R+4rBkdHrs^&8WNKq&5+MQ?TGZd^eSMEus!eom_X_fu=i5H*~U$7(OQ zqpRevq}QWDx4Br`Pp}h6`7pvzlZ608BecK?Y zkP9O-LVyEihk{T)%lU@1{mT;05BjafXMXZ+Dacf}nT`td&sm?_zIb(>Vm6gb`X^+1 zzAy^%8E#V^okO@Lqcrx;v|Jp^NqRdEhIR;Vh}oFmz&s<@`|DO8DEdTpSo&GjJAFDX zcsG|;C&1tT>3g5=jwPDmKkCE<4FtsaKT#(!PggU0m;a|tYMaWaGHBbuf}%o7$YNc2 z2TYzuyrZ3zB|+*^{RzZqh&^mc=9by6r5!$ncZCm;Ryr*Ofk@!cN_njW z$N}!QL(fmWx#ex}VL_8$ve#8X5A?m<5TlI-On<3YVKD2T(x1thzEonTmVt04B=QhL z2p_{*RuLO#n5aXH_MJZ@RU7Tao0Uu8J%7_!AOs#uq&s0U`^nms2Aw*{BRwfNewtUnP*wO*I zFkTJ)m}cb|bHGW7C^i)8roGMQ!83jwEsBiNF*ulbZxwMPZBsC%rJcksxcnj2@7oH_ z16EMrf_K(>t#8vwb&U=0#W&}K9lJ}<7^(W;wcINH&@)R7);8$Xd&N5X=gq@>AP$~5 zsf&(tP6ie{(n;Purp7H>YM?Fr^z$b{iao_Y-GSiCkj8gVrl{3F<+6$%Q7+@xG3rQ< z)=tK4;?H8H9=cwW>uj1&8aot}bTU4OLPQ(T{@VmW9tK37FC5G_)Uc;BL=q)5pph3C ztCoVm@ChHfSP*RR2QN^5F~)dgLDiz8cl;2WP!;!pB))->C?nMWManw`=K_4+g0W7V ze6gJ`wr$(CZJ*dUv2EM7ZQIs~PBOol|E;OHcji{znW=v0hwgsbwfEXdw{fuIGYl-suyYZ5ouJ_iu~smk$G8N|KK` zXufN;AJTS$CA9db|M>A?Tu{zZ{7^P@;lPj{{hZ0 z6c7;J|AqIWX68n2wyyts_KTYtxw<)mP+S}1ltkQ zcaI@@hA}d|Srb94)tF7=8*0?rEr0N3QU6|_JiI(N8dS2$^@+>~4DzelYDZ(7W4iXG zO%%Xg5T3?Vf3rW&h)D*{>->cd3)q;$)F6}eeF8%QmW``Vu_<8Zf|*g6i!1k5c%`{v(Kb)# zDW9UNP<&Z^x{%?fmSSD?OP1TB$M78?6dSKQjAEtpkbdnmy9w$p6- z25G4!Gp?BV9{&uwF!Ho3nbNORRV{F-)r~4yRT8`9j<|Ijc{(69R$yVBo=6o@pp_nG zp_ftF9_f-fT6_+}NB3J|i3tt7u22*fwog|lg&7p&N6+moCXAJ_@DO9^3h9tiSLf?2Uy4HFQ0)-S!_%AGw*=ygWk#{mTy| z-Wq}^%YentOab{u13?E@N-0{MwwQcGe<}xZD3-!2der#@SSVF1-Xg>fN@vBPI4$@p zG@OWlFuOPYC$=xBK9vNWg#mLu^;6SS4%{pO8WLt)Ej}6bs`lE0zi+`oXSX}+(XNGr z9y9gQCCGV%I%^NCUrNn8Xda7CG&@_CeoCAHi}x1jqAD1oDyXZ0K0LXA>rKNWoPyYS z2N&*zn?H0BE^bSiI+=({Lxn}nb?*@I)*h+^wc<6Sc&O`JrxAp@V^Jas5=KBr($pZk zjhCPzI$#z(M9QbhN@lzIYi(ELA>!^n(CfVDj_xt+GfCCb!*y(?+Ug8o&{=*~DL(%_ z=~EyFzvb0?M{n&tNS8LUkkPQ|x}m#Oygm0yVY75-<~m&Il4M`c@>hd&Ab$JF9Fhm4 z-Hy=Ab-#&pHvOzae}Id)K>Qphv8T7wD@r3n?v566Y2$}d61P8*=IoY-%8R&AKo70_ zT6CNaD9^$@1=ScDZ$HrvQBj0fM0;xm`OD+p(e5TBRpLjl&&9&rRAUNQ>BKACC&*J^ zh@@&dycOzS$b6_L((T83*MHPpdKQzt6fLgo{V2(ex)hvPYB@)RxbR`6-4h4F9eL_3 zqv$+!PDP$N?*$%-hakn|dVAmfMCk7)Y9 zsI{fgikMh3mk>f=)!)PkHlA@W_=m;F1v+lAyDNB|2M5Ei@6Dq#`Vl9vezbnDU}FvEBqL-UtCz68UDTCw!RMTUx~Dm0qYTRPI?|v+@L_ zRf#b#GN(iifzFQ;rE8Hg=Q5!wiS;Tulfx|$q}YVA1k&|r;jQ@#_BH|6%r~h9C^69> zqFjO@H39@U>9vZSTZ?(7BF8n0ajrlGqSmyM#Gnj%>V%vzqL3a(Mmb1D6@8>Z38tB1 zWU^D@W>GqEZ&u4Hg2IXsE}lO3)%##)$xN*)YYRJ=Uy(2m%cD&A#)OI4>Im-%h<4f^ zfQAx8Ac62{IiKg8@Pz8Y9Qt&$=3}xU)_L*2zsQA+F4hM5QOiYVJd%l66M|&1? zq-?ZANC*i2kPzsM+6N}1W4DqJ)rImOnIhFI>Tb@LJtZ2 zr}AAP9i8iysq+Lkq4&8kH%{pOeE;k_0nXSGX(rzk4qQY%66m-w905DaF~8nW7c?Tj zF?N2y|9cF-G?tUq{||=0VEk7{@c#nC|DOqQiH4OAni{%4rJit-Q57waDm19nig>>^ zS=dS_iY~cT6+*Ohf;ESGW;jEt*gpRd3`n1U7(_+iPDB!=Eb#WWL3nb+#j-R(#v-Ef zFTv$5+h_JaE6v5>`}-_j0CYQe&$v8v_4mqnlP3_44+`8I4-#FA7k>;zR{TL055Y5) zZLYyHcjOmsTqEb`p%?~3gJ;Hw6GmOVCtKXwy*D1EBeS^BuHiU`R`&>aS)nISoX5RA z{L}-%R-U<2*vMz~Cs7!Z6MIyA;hUx)Yn{!J^Co(IfyPo(MK!&dDi`@lN^&BL86RZ@ z^%&WTLmOY5f6;HK_Xz4Uc(Ey1%c|$Os?h307G<82!exh-HaVRn8^5kZm1#uKaI_{w z-YiMFAu#K%<``N-|GLB^4*-R@sh=c{lGM_ARSCJ9<|5$RJ-OvH-56#a9b-hUPKO`` zu2L&%v*aYodA1pr{yb@hiEfGEVlhOP0K#iZFi@QG*l0wNO4WkCECV%bY5j25X^>0! z9OHzbia5PPYvYw%f3=#4P|6!+o0U+)+LesRcPgtj19qs!pd~Mx+vaO zxKvGshb|^#r=dzV)FN+F(FY(w&es}#MQ)E2u6a{aJqGo#hr;|2L8@r2mW3I=_FULd5IIwyD? z`+Bjrb@IHsZcKz~8m!p6@kyG;4-?-TG#Yi6g~jqxntsY%JXCK(+;!TMa)d>{YLuvY zq<85^5NF^+oJFvs{IQ+OnpCl8{`u;(vuuf;xaQl>T6#1|e3Gru*h*S8!L3a6S;0Wm z<5RDyJqp7NJ-AKIVqp1Nq`8TcIG>(op`aVCxJv;u;1;t=L==YX= zdRqiTgOn)rZ1noH@h@&MTQSV&8xr?>3#5S8`!jjo!NN2#19XGPH=I3DC#B)GYhEa^ zVUIWcVWf7lLkZnim4%v@;tn|k*}vNsLg)gK?Lm2Bx@PT^j+&yG7UHAj5eD_tQ3hO- zY@oP5V=TXG_Y%51!c9FLUM4$e`U)#zGUdrViQfYTT^^usUXp`=~zj$ zCGAN4Zmk*OEtUk6YsK}efvzS9N2w-&jxq#)!6hk%qW;}=uAu4Wd4vEBcY`bt2PZmGjLaNA^iONkFKhBjkqL9srXFi=|J4*h@u77#S&r>>jZ z?>g~@#InwAtS*Q*G}??&B*tuAn6@Q5l_3KdL%G1dg@@3MgBZlaDR)Zb;WvAY93Qav zJHZav!v`W+`fp%Dwu}dwwUX?HMr_+V{6qMWgr~*wv!=DeYNO23F@>yXMyd-yt)WFy z3vsukWfpD>9gqfc@5cO-H~k9Mku;3&U`%@Z=e`Vyo^lmWfBoB#T8>kl>!wWhDkAhr z-n4z0zKBGb{-O|gq6E)a`XoC(r+Xyq@qLkq&*hc3+EfYbS|6=>*vAld^f2Ox{o7^L zWUfHHNH2@|2UHo+cswK8uGr|w44sKNl-xHvRkqhlC%XsBo#G7S z`qC@9UTP?~t2NYMsjOU!p4UEZJ_3eaIHVCcqv;#a=Qq)6FD$y2)w<;}d6 zn^2I+_U~1zPSH6}%a+b&ZKrXWs{}xn{>A{bedter4g#+u>Ixz*@d?qg(_+f(e?Z%< zmZj$o{|tI^hy#}29P>Q)hnW%bBN$oMxa7ACz)Kfi5G191aRuO1NB=WjS6S@FwSO7) zU_fjk7M^XqkWAYJsx#GFThxL2x6%SOi&1_AQC*=Vm+@Z+cA0pKx#6Jm+`nJ{J$Nre z9wb^rfq)eKhkzUXe*y3RbNoj3e>qY%b8)bB`#&7E9R73Y@}DavUjNN7P{T$8O#^+0 z0|I)S208O?%%x3VT)TZgbFewF_VaHDOe z^)2yBZF^rGq-Kb z7E5BqFouM2n7vG8ZTb#`F*de}dKdt4Q6I)wW;Iz1Bv2N1B zntVxpjC`ZDu)%l97n#@&Cn?GnQ>*5()%Y?I<3+YG*naCQcIlE0T1~G7iREjx75GA< z{y8|QuPOTpBf@A-h8~v5DFkn-Y!cQJMKHE6cR12=AT~m_2#w@YY{zURr2$9=m%mQ|jWIOf5 zy{LTomF6|%4_2})bqO+w-3DXwf*o5)OnIvGv5)IcJ?4_Rh;L%Qxs}Hllb^f@V-`y) z8jG3=-lf$5y#XE6zf~ z0`K+I;|hQ3n~~_NhiygCc#$H<&7oH$hm=JtyIRLNav_ITYN}^Q55tm_C()YgB}JRm z_0ELF#G19(DsSV+G%UncE~7Z>#M~IftxN2!dz0H_>4^U+HOw1*p8pH#`oA?bFpBc+BrHprHM9==pQH z&Ow*7ttM&-oQGo?oT&e0yJ2$IS8BHeW(0H9IChpX|BcMsPjl^iVvEG9v&$Hq0}2o} zTBFoiH}Qhj?osJg@O@#sDGE2Xz*xM6a(1?4g81|4-)XjLAB=xK>KJ^kXsv~p5ljJ? z0poCLy^feW4>QiDv`?)Uq%9U=HGj1#2AP&RZB?)l>09!jT>P}fxc}2Iov-Q<@rP|Fb<&RM`n~`aANK(_1G`o?VoxSSGiMQnl=0;Q% zlYp1OBVNZ{C9N|idSy)S{%6u}XcB@$8>*XZ&2;5LCu+9T zgmOa}ycNa@kWl3gO3=N9DdTO%Qf8|#fk%;Z(Ds(^;$k$Bs*#rKSLo7ZVgG$~jtl$u zua=wdjr3xxp~@P?N>&Un1#RUh+~{u4V{DkWT%*q@USZ9-wjvr=Qa?B)GZADX|Gve) z3h4$7@-l7JON(MMH#m(p;=lZMZOh7Ro@-R(Z%W5i>c?ljAV~0^sG?nGNwKV8W{bWg zG7<7qw&t}Nv4v6v$yMr8V@>#E3IpW~!=J=Xi3$*SK+DE57|X^wAhHQ8R8F5c^N6_1 z`{*T0iAd*JEy2#8%Wn%)nX&br=U{&>P~^q@Niyd+OCVuD=S_%o8-7oM2%8{!YCgg}$vNAG|Sgc+WJtwJ+S`YE2i4|4$W0-$2Pt9{2%B~WIM|{|KOCKK<`yjo*AmUou6B18`yyCnB z=_Z`DtzBp!zGAX!H}@6ab{cnbR116f$$R{B?qo>1r78Y2S@cVBH6YztllqIW=$GWW zr>*`MS8E4a`U>&>L*z%dO^lAiW^#lo&)q^It|F_%$bQzIboe%5L65c32eB&RSJKQZ z#o_pS=y*zaqzuT*@MXkM{Fx~iDLA6HYl$v5ceG1~f-XVAg2>_H`JPk3UzzwR6zlgu zI3>y8;e!$o|MA-?B*b+Si#qQ$+Aa+kCcGjqB#zUNxAvxI2$Z8@a8-Hw{2VIY!M3+3 zX_;-(9fekK&RC&3jwly>g^9S>qwW8}F6z)`EA$Z)1myfb6jkv57tsB;X)`yhFZ#*L zogbHZ6Azar1Uw9FctR4SSS?7R;BP@NNiZx3r$!zr4E8Q33y$A6eI9l;IzC&{H7%{` zTbt@Sun?7nt<8l!4nBL~J=Ybht3N(_)3dX;vlPEx)AnW`{PukIy5b$2Z~Mv!zjtGgoH#?KdEdk3>Xz<16V~>p=%+_k@va{Zt+a=sRKFag zw8t^j@Y@c(z}epqfZ6^X#B?GxV1Gc&^0I;!usfJiXK**h6tFv*a%Z^huR`Cx2o^AI z3*zfOp=Y2S*@3$~0BHCaalJm8G4$yl1=DYz4gK@134->$r|gy&nhEyP*9<2ntR7)R zx`dyfQvfHxJN5~|#5*n%@r`^G7l9k~#Tqjr{Q6b**D&ZV3^OTR*-><0NdJ658;RiH zQ>ho(Wyceo!3Wzw|90qSNW=3!MRv353&fKYsV~rAuY5m?_ntTUih}U@AdBw}gP|AT zaGKWk@ak|@=J9r)^G46mi@SF;p!4-RrVwfGY(VGTKZfD%@4g-UJt(Aug_Hk$w8wsU zwfG<38z0lp7^1?BOo#<8t?t>)|J92Dw%tU-x z^0HxG38IA*V+rl5DAVX1Dk#9i#$qfC9Cu5{$Nk1upuVQ13csNBiHH~m)sZ!Q9{QPd z0AHKAg3_1{MxQiSI^@}&Pip=p=V4gTk*FZF$XgLN4k9VjX&lDAENy;Pica_w#n6nI zAU+K0qQyrOiMZNnkN^)GZu4nCGKOQRQ0TO140hFMhOyUkPlzvhyF4`U0fLELAT71P zQ!^f_jYos&A8U4$b);mX;pxdBBfj1%*zO-epzS1bCY(x+1Z^O2D3bFozMjm3CQHgj z$}!x_qrN=kh}cAgEU=ytvr|AAiM2$g7sgdnfh?o1ve=$X>aQszg=GmBBc1};iBISs zn3{Mb1RK$1@(#<4@Ez|#qAFrtYh@YudW@Ns6m1~(-5(Jh@i1gZ2I2QhN!mo+==AYsqs>$?RJUUOiVz>4sa7}%-gZ|<8LuV_AYvre>&r4-lngml%Ii_rL&9tRi zwwPdrT#6$8L85fg(e(ItvUO+jwittZxdcHcvxZ!jr)8*iyc7r0JwrK-0RzK$BLYKC z3fKA1<~6T^qw?tfv|9X7OZ7kWuWq@rsb;CNs}KvxXt56|*IMZWR_n-lLsLwSzNlJ3NG_^(T=V4|kF#lc{w44*?LK5<{&KV6*3xxZS? z>2}bh5T$EI6QP_GXS$T_ZR0ORSY=ShRl-vx#XlyRWA?piP_0wh)UqIB{(#cQDpk+8 zn@3}7+d(g?Psz(5X!TZe=Aj!&(dwZ;mqBVG!&1h|0$7<%i5-`r00l94YbU2!F?czW zok#yBhO~v-MlJ>0S~Y#-RnASV?4pAV6z{8p3{>xBV0Of|#2Vg~l$x>>#oRC|hGI4w zXMP4D6wQKdNo|O#%E@?Ng}wV&)0FiwcaaH6`j|}w-m!^ssLXdR)1(=rznns_)xX!S z3yIq40I`jG%nIQX4z20EjbcTv+T3K(>-`W5BnZ}sSg6S+@@Yk`z02ft?SMJpS*Ps3 zZYl$fBaQhRv6A%22V2@v5K7@8D|@bZJj3G|2-d9s|dt8Yji&5Ga~v9dkm`-XA~6){X$mDHBQS@sN6jCP1d z?>1H@He*xB5`5U5ga)gN_cpO?o2#(qB0uj>MY0V56Z|IWuTLnybfEQ2^KWW<*2c#c z=ULUXv-A2O7 z)7mgHTx*1qj;~v8G+xuI_677uFq#bslnOPu_XpSqBbDES{E0CbbD(;Y1N1?bQOP>* z)$F&aeIfd5jNBdkow&s3>?opOg8pO`X``zi!?P`7Qi<3WSkiPEv~^PTg$odD(MGg` z-MD6K&AFh|`GEUY93ec2SNnoZQX4&wsLiWW&FD1z2pD-6)rKRk*C0xP0?tkgnDDpi%}tW}w$-l#2E zAyt{Qbc-XXE7O6cN|dQX780^yGkRgU`zEQcq{M#fm#5Aq$~r()gez4^JY9z96v56c z7|j*O{CHvQ$$4;*;+}CwRV3Su9l*G0J%|z>7W>gx(1?tX?U zZFf|8pXPGoB4vd!_mopuyC@2a8dG#mF+~@5WtBz)6y<`a4sR}NBCOpyZ!}Zo)W|iC zXLF}8g!M39qp33l6Xula8=^0!(dyesGVnC=y|NuWj{=|_8%=^yZd_2VBF5K|Yl$3N zX*pO1Sg4K?BPov5Y}G}>fvpiWFjL^+Y^2DG zh09Y=nz;|E%n5k1xGIuJ!=EQSG?XUN(dY{mQS=Q62bHS_2j>gFQ|2cdZvhRFyM3Pk9~41|e}L`eB~9 zL~W(g%qYyA+kND-qY~TXQ?Lgp0(iubMyx3Ae&?#6fXbjkAJ@+$lekwHx2*@)zYM}2 z!AQ9YMSa>-NoOQ;ltxt@aGs3YG@s&XXZ@wO*YD_48WCEEuD=|lLoYDOhGTSicpjv5 z(>e*o9x7jVrsMf#Q3CtnS;-h(Rf2K~SWqK@m}^>RRdAGpfLiPFlFRwjNZt;bHv?So zX8YVn??+#R^B9XOllF;d9#rd{a8#t)=*sKv$ZOF=+{)ijY1P{>0FiR(%xBE#-rbD8 zNuCU~Hyiw{a|{mK^Q=jeL*A#oGi;U;Y=|P?Btv{d+tQHe8ug-M6MQ879A<3r9)+R= z?9Xm@*nNT({GSY2N{u&adv@)Q@y5tI=)DQ%OK&&hoY(q0r&vHW{GP@@YisbWCuV@5 zv%Dup8E#QyWnv@v-e+9tO?h@+uzU2>r(!Y99#0RI2AR(4a&e3T1|ss{k~Hj$3pkCF zpmFe+D;-fQNik(6IU4PY_d9kBgSOwyfBatak|=N=t!Tce4J)IQ|28hu69X6Nc1J~hsVtIO3k5i0TsS$l zz9US~_eU_9+>1X7QA`qwhT*^YS@5Xq*$w^B$wU%`T+^?C78BYW_R(f!at$wZ-gv-j zKxlb+GmIRe4E>yj<{#*!(9f4aL-hOhTQ-$=nHQ!8qpY!`67gdwJ%6k#qU7Pkmjy#@ z?y$-EWqbv)mwH$w4*BiESsgH^$jAiK=a5LG$9^{u8b~n`Qd4SCRP84XtRd z!DgTbqAyC3pQWb1YTP|yvh{{iwym4yQS)Y=z~tUi`P6+>Pteb&%(aN7L7LC1UvD1I z`WNtqY_~QPYDrDh3A)8yH;+RIf@wi+qCRB-(Q34)$7eEFDREef#i59&t>CcLcGqazuPU6Hgu6@TG>azBeKGo={l^^pR4 zT>wTcy&GI#bz1^c4x(**RQbcDd>BODXOrG=nWdZz+v#@oW@Dr$@#kG5(hA)7n#bPg z@G*WYs$CwC2~%~C7hvQI8b>3nAtn&eo{=?x78yH?bTJ@rSI$uFb|FdBJK0Sg z#9P$OLLiWgBks8<6%pFc`@UB_BI!Ts>vHmjP5vw4YU)=OYa1S6D}R7{kGK^H$&j#{ zJP~nxB-RBfhnQ$}(mw2lSUTG`cRDzS2pds10?>$?@C*ERUf;#!=YN_PuK>br;N95% zzej&uU%_36aHPpk{Ci@rzNO9p&0WZVo#emq+JCza2s-CV{_#T*hNHeg^!$U8zCa?{ zbp(^fk&YL!5;?+3CXNx}8gx=@G~nB)10ckX4Q$Q3;`t)Xj0v@v$BUSW41p_N)B(Ma zE64InA!hrFkM_^RlB~+2Z_^-lWY)}vx%>G^3Ne9(0=n>PgTkDGM{7dAEb9?J^6`c1 z@dxvl_Jq#EvEchDVJip7@cAM0pb`{lQIi#DMTzt513x3Qb{iT)FhJx$5x{)I{7;&{ zniA8YBW#nIJ7QB=68#T_3i3SQOBttBs4W$6gQFq&*5KAfRC2Q~us4orUHXOT*7Qx}C&6friKkvJ%ImO^9`N#7s4$B;fm51b9as{0qA44qIqs0J$6$Ta zfH#-gen3Zmoin6AGC|{S13S<<;{6Vb66e!h3CtJTUC zb`5=kKnc2)R_#i1OwvK|0%8+NeXa-l`-Dh~WE~@#HZX$VIg8}a^w9TT7^f{5r_S!X z9UYbq@Vgbf#k44ytA`uF#QX2Q%nI z7Urd<&G$q-xU;dx4x9{DYyi1@GT*-O?)!S~F+kAI>*jB=5^o(A?zUQewX4^8FIZ`B*ov+Ive%8e6ogJN)`@iGcl{oE)01hxd{WoNaiytDPJutLL0*f21>2+dex8$q^X7I2Qd?;R;3d!YRCFC2V+y^eg31^7 zNzBN-Zty?gZv_s75KuRoS5xEhjCLR+?*94s?8=b|&?`qP2U1zj#{+ zTmmUAK<$?OX_{B1RdKl(=ATQI{kNv0E*TfbFX&cXtFm`9Gt47XrG;$CPC%u4!%R71 zi#d`wbD;%-xZjv)!wnK~zv0*i>I#y88Q}LmSRrN9Hs68DBYD)uKcUun7UO0=^pG9# zWX-AK0iyLM5HE0VuwTi??2=|)%ke$3x zgv2X2p)D|C$*doP^S|(4E6#Tgi>cQtpR!AL#e;=5@U5`t#n*i~nFsNL!2!^G1|#pb z7Hd}Lw|oeEJ(PxN0e`8ktqS+3bp61*2}{M6I|#r9_;iOV90|{AYd`P#b>5mJIYH(U z;c|nq?|E0uekg|gKwAd`VAR+7dlSrm#0OE8$4fyd7b^RP1SE;92GU7?ip#>yJOK=X zSztez=4(cC(u7{KuZ?vs!ug*CP(R;jj&x1FsKYhL<7VIJQt4RjaWdO($&Rc6mhgkR zb}tROr3;w0uLMo&TYLM2nZJ9|XLilr zuSYU^jFNh;$TeVT$LHaVEh(5-qp{$SMO-2ZI)>*C;@PaWFT-ds?xQXY|L=3`AGq zP1J3=zgp9zB9J%#E5x zgb}LyolTcA5u@itPIZKFs*@0uDuRWy70ho2^W%$)6VZpf6QD9)QavOU6bGY!f~B}5Zmx6>!4GPRU#!crsZriQNvxda%d95 z3_lvpVp9|}?{l^Z%ml$2_3_WSAsV+_Vkmh~`gYo}OYW^a?3q?C(-_}dl61E|px;-`0P6grd#E#GUggI9C?u|@Y`IPpLK*-BX)m#DhAiuka(8?wgl z0z2A&T{?0=I3WROG?M&ZxnmqlY6eo&M;nIg2Z_UQkUko?=hPxb@!TumJ=)<$D9zV` zF*lSf;<5|gt^%f{Jg+Q_j{PrWupx8`Aj%{MWJ5j1;nkt2QD?TOP4^;-JQHs@7XA^J z_b+h*JE<*RB0*7tw0&DLknh=^edu}-4IKD-*1SI;cBAvY0{(kFq0_#atq2qZgc}?L zg#Uj*Pmp%ElQpupa5J(nQ**Vlbx|>McDFJybNMedAxBwB0Zj<`yEU008K{VYUIXi( zhQ?0Di^YT!v@nbp7V+5IgBx$x+BS2m|C6R5PWT1-MRj=HqP=|o`!6F4&(loW`|0W7 z<0lfx=Xpy*L@b1dMtx)r#9!2HOv|465<~__jF0#^h)fA~%D@%XwC7$NgmFJr@SzNQ zYZkNFyCA6x>AX>XUWVQ2L%_Ga?Nj>7JZ;1u$9~@Jwxaq%a}p zD*i2KIHgp;cO;*UuZ-cJ0`dB(;T7QWPAxe5oAA=TEU5KFD0$vVgBi?+g91g9r_|B@ z3GHM#qCGg=DsOYr>{(o5r|0VV8gWI`ZC;q<{xY)!p0em2ukNC$QuZxc0a}iKz#SUt zUU7fpr}pn0@i)pH3l9YY610*x#_nppNtQ;~Dg0yD)sc*{7k&ML!Vz=v!NBn!rQN5! z0mMw-&<7jAVZH{v!!%Qdbq4--xQ7yYh9|ML9YIS$0`99nJYaYOoQLy^> z&-d}Pw=3J{)#cU;)4=oJxC%(UsP8tc8mG*_Q8tldDt`Rg-UdA{Xw?m8xWOs+gJ`wW z0dMwP4J3W_2sh<+)XSHpcdZCwL_9-9yJ9Gnlq^4-vPZo$9qJP!D`>-NF7 zpNvumgXQm!B)0Pp|4p}rzq!%35x5_adiKY~Z$2>b_)o_bL;ce%+NZqsOvnmQ#DARj`=bx&}J6tmm9kFWlJ@mf@7UW6mD|XWLP2phf z!D0CT#k;6@`13#fc0_`Fx3dWfzid}Y{QWt2>>6^usRj>c6;0Q9@D`xJJ2ifN_8bWM zt`o+1{~JgrfVBp_>*OQl6W_z(LtZ7Ffl=`**iQ)HBv{Z0CFv>f94`BN$rMs%-Bd8r ze12Qb!BxWL<2$Z^VA8%rxZu~IXu52j3p$301N#(|8fZyd_-<@B`pMZeqqTF8E`T0$ zDtj9l;=V9CGc7E^Dkl5l*^F#b#Fb8Zd{0Ce3lkMdGB~&hQ(8hCj2BXxSWa0JY4yiQ z+*O=ko=DYh-h@Xam`__2cR>ot64#=Ofa16%oj+oK108Afi}b@hU|C^v85KUvI5Tpt zc~)Tu7!Cx%5by-*z{TYfJ;H$GRx_@$pFcKVGWBF*vEsr$U%}oXl;h$k zEa^2aV|iiu%&(CiA!s#`S29`4)9x84km5s;lJh*bsWi?II_unX`8VuwNG)V~rs;l1 zXx^;l+}{6&gy_VjL(0GI*h*YccU{$+DERMFT?U2iqFHb}m~G46qa!EehuDZ{XRdN% za<*QG=?EfT%xi7Ln+d_IImMfer56vJzwD9TW5&JI>}oyeQ!J=6skMixB$Eiv2!CIn%Z{8_D+)H3hgS7)aQ+elcO>0krBoKM zl3#Tzp5luEnYSzO++z=hv~}L!f=3qh$=!2zePr5uVc# zR{=wytAB9vh$V(BTFs<6oeo8?W{g&{Dyl-~lL=0LaF2eWWhY4G(LNogNZtNvC+kfv z{rLF>L>D2%S-a28dz)TxpT3;L1!`w%s8}_DnP8ntyzMnzpHSW)IhKv@f1YejpH3>%mZ!qbA%{6j&MY~R zDyCJI9_3K@2QTUTqg3impp3jgC|wbH7{xeuD&*@5iftG4+Q0{-yQ>+grS^p!S9R ztvg^){wDL464f`lNA;!y2ng@fI3V`a42>V6RQ1I+5GtP>P43hdDZuKQs(&z|;BEl> zQ49?z&eKr)Li^Sl=qrEI_{xe382e8#7U=kE0M}c3*au0ne8RVwj0@6(sN@QAWM$7ae1~Xw$w{4$^BKs)dLEFj)d)F}jr>o2?5XXqQ4U#(21*M*UZD_t&&!X7M&$}mm^ z#tmtxI`KNGPLFC-M(jJMtB}hpinXK{MRZhhDlN;>Vm?(4`>N3*w^Wv+y;NHEr>QPb zWuiAI0`!GGbW~zmG+>z;gtJx2F{%zt8$i`a)@x`+`t-)z?bUcRBmC8mf3PwQ+KhYJ+sO z`Up;gI^fFl-}cwlmgN4>BaPOHp*M`u$cK}9Yo>orNa-D0abeV~6Glsx8@`$?vR<+8 zj#{Av0C$8!?Kz5H?Kyhpy_uXZ4mqh!HL@vvLWd9L4&t90LHa~(a02-VxOE?*ml9N>q|mdP#9|1rp*ZRc(}J0Lr#+rO>kvbC6|;n za>^wksqRt=`A>BdQycd5&Ib6zbLOqysU5JrwZZ-T4geaf0x%FG5_0Mm`5!Zk28sfJU@O)gj*-P2-FZAI=jr`MJA5)ixV#ppBYSPP;w|NBN<+ zQw52=v4%$z(B4P^Yg9nFyDh3>Vh*~pU9DRKaEErMF>D7DJt|||G^8bN$P|>Wbu6{I zK*d4Z@GQ35*S@Gx>!c%Ug{nb)GNYT0;pW#=?U0beOHZVV`Nl20efVS+ih+1FAQVCo z-Q7rEHYE$2>Ee}mW`kS-mto`cff-HR(%}f3&xau`Nt-1CiKNynlI%WT)Elu^>{AJ# zJLa*#ly9U8B-h;t8JDzniA#OIYv+Us6qWLebK4w$*Dw%WBPUp@?o0R+o&OKPjyQl# z02A`63mYwr`B5=s#4CXzp^JM2diZ9ekts=%avOM?e}ANNCge|d*?xC=a(`Ur^iEb2EgLaSuUFD0ixm!(dEq>uf>lcXG0h&KTVQKzMIyXf8*_A0`C)+ zowGE~79K>M3NFF}=8u=xYKHts`f7HT)3H2BE1K@b;nIDk2D);wNw7bhs0`iEcsmE) z_{;kuT-%k4!oQ9*lBmF2@t%^_gtKiYkXCrLsMdd6A-1L)38H|<= z+PTG&;TAaeOn471Zk^&5t>i@7xjQ_5xhFDP$u#s?IFp(psK$-7i5RYq@6!F2@Ir{= z{CwOPWgIDfcXxX557Z)O=Yr{>?u8O~$Yv}qh>#Sn^-^#eFs|N{ZWJS#NQ0`VwS4DC z9nix!*cLbSk%eX?FL{?HAq+(wCGuHekdqlg7V3$1Moy2UCOwsQ{B8jDaAg zzWB$*?QY5kFBdVU3=#ifyoot07#0`7mSX2xDuh}To`1ZogS7e^E~zR}SJsg(K8Rh07G64ym64PO$L7C#br zO|D&$Q!HPQJw)-y@-5>J(Y!vH@b%s4tahAAPt!P#!@?ApoE21qIp$Ax0%G#N`Xm#e zOU%T&f_3xgN+&ILp&C?DTewl9!Z#W)nr7!Gli9;0`ZJGE#B=^n&D}9r9AU=ezUO?^gV;i@txixkdc@0A18aw6KL;p8U-YG~I z=xO&G+qP{Rdu-b_-?44mwr$(C_t^IAQT|_&uPQmIlR8ON-}GA5>tfw>cXdC%QSi|f z!L8;B3d&v`@!jQrI`$5)f^3b62+85v>HRyoO0#sXu(>TUtZHRRSi>?+YaQ=2c@5xObyq7Gl^f66c;z~9NL2*wvE85oV+}Ws$2nR4$ zy55>)O3mdWj*0m}3!4e?UreqpA(QDLn=2=YO%Si|M>NjlR^q>cXRZ_qB<2kb5hPLX zSetT6bcGQ*9t1OiI)yOr^QM3L7DeaZmb26S68)yH5P=1x;ep#1n}yx-fC&rAtz`qY z4-o#j4&*vwZX6$aU$p1=X>bT27+(=nId3Bfdk$l%5-ASXE*ueUAO;mT`oZPwTv*q< zZH)a93gjp~|=c5|*86kTsPU;BXHv$i60;f*kg8t-B9*NqUbJX!w;+kGGVHPYb&t?OrLxg4488v zv<(Pz$t)>31cXXJStQVxL780v@kv*sSA>r5n}|Z17-)&B2q`-T{P4UNWkepWL#cv) zBM2faw{s8#VM7i!@0>PvTC6$n!!@_z6~@Z-prG*VB`nyA2vaTui>lE7l~kRc#zh>; zDPzuvZn+hecA6S55LfihlPsT2Sl(2GJ{!>8kVoOc3^NQbxq|~OEy)Q>cEU+pLT(ye zqAq^fwmcIN9pmB3Q#Fp+XPHg5EgMl@avDJP$O+55?LJTE%v^H8Iu(S6O-oNR z%CyFwWQjZmtArA1amr}%v%qmuNt%$8BH3WG{LThU1_B^|p;>8XByQ^hjEI}O zK*f53o!J*`#@af|DToX|!YGUiXsnScFD{ER>T8cAV_@qMY$J0;vRgGuTKa)f ze^2hTmcS{~<WU z@HEqL&PxK}ZyB5oMXU?5A-r&Ae+Y{EBRRJt47-};WhOb|r#vDXAC4M)tH(z_bEBQM z@g;vQ?Mw<5eNKRwcPY>f@hgD_TAzCm%m-gePB@3eJ0cDL!YOrt*A)W!2!iiXQa}6U zs(%F>UQ9beZtd&mz`PmpUyqyJ1KE=z4n{eXt1fN$ymM>@Mvp@MWmb*Q{#%MMwf0s) zZhG|z)+pQNN2L~_BN#;3{7>nM{2jV77eBZNSsCl=${r)CHN3fq|!fZAWG>>mx7Yj88u||BroH@ z{vjH2HvMTELut(cNA4QQ-T}p3jL0HhJJ>Y z|5zfDlYB9mbSBG<`4dH!C-(Cb16cV=B#gnDMzM@=2nZq}rTOqkV8i{95$Xr~gef#>&9NNi(rFm$=^89uBZkMw?Sa_|peF!Q3Dao4Mb3dWHrC;&gpedm&iF;ZYOENXco6 z7`i9<$6|M-A0JLQ<@27rAx~)s`-DCu(<|>VjS`Gceqx>e5$T{&?Ip-I&H`jx=zbA` z@_c-`{%5J?CWxsR;7crZ$06JhsaN5gKMo@bJjB^3UaNfYv0k>&5c*mBijQP2*kTzk zkOxljwL<2m&pkn`ZM5K?;nk*hyj<1Z7DpC_jp1=BcTNVol9g-SsC^Nid#n)a6Q+0C z`d;vYb*LBi)(qI-1NzdH5`_>90VgRTt^;B&8C@AkFEWnaNO_ zhdCjsPH2>c+cWZ0z`x+$k$yZM+17u}s=j5X*z40%CcmB_$g2ARaQ4CzZNbsYF0N%e zNT$)8GcCcDT(di+i+l}H|7kB>v@R@*%(fp1$+sMLDQ)jYy0X9~SQqhOYSI z%l=`U)eE6qG$?Lm+vHic%?;H7WN-3dx=*Yz-HP^acA?a>=-iFHWz^!{i=$5AOL@3c z>bxC<&#Xx#*Us-3{f{_|D%z(}F9;wYW3>M@9EQA+#eWVg{^vN1+VpLAR5849|7}>S zVJ9oQ&S^c#Xe1;>nZi;wWf&{!UI6+Ag-DlKGp-w_5SpS8a!&x}A8^8FG2a2_kz()% zy!iPaC4&C`*`%`UU96DwJ9Bdjf&S^4FSo}x!T2AtBKl{!j5NijJoQ8<5&2MRu_HQ~ zX|QQ4j9Bv7qGl{Q>@-$7is(_}W>YOEIt#5mL+1TcXUcxfkXQ7C?P%Cg>ydnj%Dj68L5;Psj7E=3+O3NwLq^t-UZ1$|hCTg|zqAGh> z6^#~4*&OAjtX_>tYS6iEdy#qQU3K_gy&~*wHN93|vMp>+8s+HaO$o zvhfh^ni#8ds~B8pFUmi zfMt$S-Wd1Q8PkUfaY1$GNhznqG=Kl%o8`@8elPp>lkHu@^w(LMoE0B#EsZVx!CreoBsrJKTqm>F}z(1}5{_qd=MnCM*kY+rh;buHT zW$}wwEcoTKn!^E%aRwX2UBt`f&z=!|2^SQ*;wd>1sF6bvsFRgGVdpZLeMR*mE)qq) zV&*WgdTd92 zAGG{s81wW_P|21HXp%VG%9Me)oR{}r75c?EIR`>o24eY&W!QC8b2l9P@#-_|LdAdc zRatE^a8WIZsFV7=KmSm4?g#MMf@y63aYZi4z~As*!6)?zJHh*}|1h56qa1 zf+d%029?&kWUF7gOh|5fVuodwqx&lR6n+X;hgZF{ z-MCx9o8BNv{qS=bN&U0z4g4E_Hf)FxZh`}U{($oKK=4eF8JBS)2k#?(P!u)ml9<-# zhZlr1e@dQ2p3|jAXz7EF7g8GTo1R1tjPo)fH}^(L1~K}e2G0B6E9D%W$ObrfJr+mU zfKxE}w4e#3AFPVq!)aT9>@$18_w0rOb(C97>CQ~Der`p}>zab7EbqfpLsnb(ssvXn z-lK4MrQh>CgP0q@MHJ+}Dtv6;VPR1nojL0ZG6wo@uFr#>eAzz!^F;T8Zx)2RcW&62 z7aAW>#1_o%_0xy^4Xe)|KKaTydwHUG#xVbjaPM5QF^@bBR96w`H~03>6Y{IO&+jk^ zH$rQF)CYFPy$?$_S#Kr|Wk%Sc<*$f8(4&L_8pC!V0e#om!#RPX;D=$y6qtWBhBs(8 z;tZoe2Gj3>BQt*7oo4PBIpW2Klr~R$9bl7;{x@1(c4_J}?v8%$a#fh{4`I+NZyip2 zMdr`Ib#k0q=-7Qu;371iAW2_{>XEe*dUp#!_YaM4*cm#XgVJ~Z|D5%R`#(=?)^2wH z56t8LHI(fCJ@kKyiktw9F8{*@0%Dc{0^lW|5}izBLeTPu6Y`=6P-MjW8*`zI#+1YdP&S@+w29<}q>&<>@fcIc(t%;s3u#Nr zR0)S%io*%ycC@d?Tw&_mhf(=j0>e4F9$Yy2W@pVg+d2-Y z?b}$AI2e0^Zg%*CyR$o13k@mjn=%P?_>cx_V=iTX+G2IKJ6$o_=gbqF(fHP*dGGna z*%5OhqIWh%?-FpkKWe=jAiaAV;6Bmdx&h^w?nWcg-y3y?E3h_9r(+$s^KXVA$uMPn z4G7D8ecRFAj`f(3j)d(DRN?AA`9YXn2N9TzL(#kUd@2BFL536Pbk9N*#3P1l0LeBIfdJkIXW703K{ju$xdpL(z zH_p9-Fh<8GdB2^}nST|ycc*XsU+)>1f^qr#xqH(<`Nxf)JMYwf{#k@?Pfhs;YoIU! z=VSAaD2Sd9Mn0m0!S-J{m<&VTUmY>TgeU4Tl{m*3fI}pL*F$8yN8m$LeIJzk$G`!L z4CZ;3y0e{kzGDBt)Wc|fALRa${dC{`q56l3b^dqwuQFVN$AQ>)dhq_6u{+17ZWzD# za^HMjK|}rT1RcT0{poiT#vd>Pz>z}le+kGCrSI42?*D6C7;?n3#o=IWxwW{mv$C|Z zwb^JQ^IZo2Qk%=GeWjD-=3=Yg?KYeV)pwe^WlxW_SkyZhr%Hl%qZi3kNQ@y@)?#TL zjUjafMXpqvuYWSB$%DMa`-~RU&z*J-|4Pwv8$qygcwi_kb1MzO%Yqal25$wZrNx6T zAM!#e9MYuDg^>t?_~R+j!C6(h1U=;Ibi1zhaemVg1fMxKFtr+D5!J_8D$%`-+IZ_i zg&rq{YrGqR(oRbCAZlAqKoW#`W&$Un;zd#=7DOT0ubda7Pk1Fssd_tW|3$Jh$@=gUg*?4cEravFvB4^SJ($-k)hA65jAH2BEVLsMv zyu`B~f7X&_9u)h>Q>!S=PI0Jm30$M4LI-DVLV~Og3BFwD=S!QVDUnR&Vz%Yg>iF=8 zU~GQsX-Zf`n(SDsDj|GkLK`vTAP2T;BTu%oEosf*ZexPo2vdfsTZye27mj?b_=bv# zrAKk5Y94T`R$;q?K|^TFWE47opYRk((M4bByMrgm>9jj*9dsI7v#*wADj9GzV5Oc3jr5NdZ- z$;w67fk#&(QdWZOH2VA>KGGs+d}{iZYO(0)zMy)V_pG3PmHRiCAE80PB$oE`%05l^ z?ldAn`Ok8>zfW~94sZ-!Iaxflylcnjn|kbDodd;)N2u4rISUH4akg05OV#C00Xk+S z*=GZ};PQ!0beXpo(05`Pm9Fj%Gc=MGbda<|r$zjl4^gAC8Awxl60xj`R3?9>qjXz$ zIif)gsDvPD*0=HZ+F{)8_pnVKyctNA>caFzW(eZy5DZi z^Ks>{rHeB7L&!;&5@g?+dFRT@ige--t8Sh8!_9~2t(T;OcaUbc`FzJ@nFA2AK@L6<_Ux%L1Wi=C*|Cw+W z4-}>w#+_yku~TU?oe*)AO+!X7pj_5)qW3Irb~-}?d2g1{ehCGY6w!x1{F6m5eCEr~ z(LDx*gzGGW0T~gugF=C5Y%Y3rcez%TyCe0Y%n|vJ=^7{Ol*cL?nGGsI$2>{xaLAG62wt0;VarRo=ky$I4c)t9BMxWu?gG5gK&;V7A4cPmv%`Q{uGma0=!eMk9{4pkWTrP)Eu zS2_UpPz(*e=SC+8lTiI)?UO%E3b)9$)g~jZv0Nxx)SPm_dwS;%eS5k_eUgD!U*Z0zlts|d_oWRF|Qup?#qk8Frp+-pQZ`@S7| zUI67JY<4wWOk%(Cffcv87ozhuwTJR(yOPoaJ}?cRj9%bM=o!}g@uEuattr_?%?<8E zGpriafwEX66}eU<3;GXSef2$GVR*mO6}bI(Qi~+jkY2|ivbDKbO7&3i@1Kl3!oKyA zVdN%4mi&*Zdl>XWA3yO@Rc2sxF@}N^Ko22XGO0(D!n%TckoWFQ5p<6j^pEmNeR6?Vzr7(tww#ss;UChMs<-0 zM{SV|Xi%)W@*b|*xpbG{%jpZYCfvZZ)?hlUIwRb-N>f6dXsvn5mWUJ`R}Ii*seT{^ zVV78NN?kysjol*GQmq-7N|W}mUeu!;2ura%|MFbLC1R^A3i&~^80q+>DT5l4I*T^= z%{-m&7LMvgg)WnD_ZU6{*S)P!m#(!rB(R}eBSs@HoKM5ioYP)mT+O9|ysg@(m!n3V zrb=~qEuB*x_ex4RU`P)Jv%QRc0*; zM5j|kZCpSEBLy+-L+ns1tMbv|s-p;0sou4jYGXE~s=1z`4rabaWlu>) z!%mZZnM7OScEPSgHWHTi3P8d;y1*aG{M8BdH(EMS1ji6xHA2*xDb_OiZHpg;LgLt)QeG%SAWH>zTX73ILm%ErEAJo^E-foyE)KZLXd~W zH=8P2PS)zrlDQz&t22DLG5+;aRKCNL(Xt8^N;+^u4hTW68P>=EF zAg4_sbFIhT4~gW9#;}OeG91-q&WNPJRiH@&EPM>|i-;N_C+pTr9M%x1u!z&fhE1^^ ziJnb8?0}#H5IEDaYhwm#&?OC9iwQtC8C_2?p?UuG#+~3lne16=$Q)nAUb1y^AYVzG zS8!Kr783@$bHx$2c3@mCcZy$IyxDBStY=B}EfdjE)1&N3{NvzGx#QpRiWyi*lYbd2 z5y;hi259ZEFD5n4v&et3JhU^>#9`INE=T?6gP-92-QD#=I7(*{v(fAKc-o1UoeC(t zYm9`pS{=N;%jV#1u5fwWUF>Zfwv_&Ec3KT*&F0FXMMVpVVO@?jk%Z!2@d`T}B&IFn zAY>C>aqvs=x02_;P1BxjWi{$b%*a!^HV0j-po~1cMx+(3^!balNGK#cQN+|9^%1&8 z>^wehJbJuL=6KH}iyLox(R&X`@y=F-jc`+EW8>N$|I(2?KP{(}v?sZik|y#IngF^D zCrAaT>hQ&x+dTmB9dVnw*Z90D8fBqefo5l{1kP=Qu&*V>k6pVp7^NUoWQ`O~{GOCi zp#`$jqDyJw{MgbVvoZfHv$-w+ zBsL$yAlqX(TxT_7S}sm@bVlE55Jo~GIyU2{Y5&+~<#=(jx3aZy|6^@8=a!bFoXeYS zq@UP@fNM|h!N-l(yOV0$YB%4yitM8U@upu= zT&t*yq_Ho=yhc;@;vE()v_=Hj+T{1LcCjI7a z=CSf#2n$=MX+c*k431yv%_$yV4oa)r)_Wq)j2o<9vnl-a_NAw=1E_Ru_(n-l#7w6N zL2W<)3(I)TQ60mGp5}w6VSY#6Qktr8GzIrH?>BDM#4joa-Fl`Fjo3b6EEa-EicgCr zAY>UPqJfmw$%GS0+_#<}X;V{;nD>%(hkm_@`>qPGGD$&#Q0Htb(7^n5N2Vp=5f#i6 zPi~c+@r@W{WNH6|h%Ea1oRz~nqDfNfj*G#*X_Qfx6<#HEy5W~eg6@8WOIV*5!wubV zd_1RXMtV(kN@m5yjec^u-t*ZXO%$PtNVvO6X3To^c1}obKXtZ7ZhX-J*b-|s;gbuh z#cq}7B>E7P;%{Qo7YltjAB_ks>5Hz96jmJ=hs*%vgoGu_eLt)Od?B8@4WF&zQobqU z9po{|*rWBMt)o&4)HmfH%8Y5jU6S=Va?kpMQRc?GjLENex70zgm@vw(dx(Nyr_<`btS2X&=2O-o8$r3txx^mG1mm=s}ZVvF{uT%Tg+c zz7LFgPoS|!X|FmYHO`D$<rg-so@wZDMvrm4}wVzYglDS=; zUoE_&I%2CbPc>~Zud=&{r6W_du!wwC?NcZOjz|E#;(HbR&9@-|KsEcN4Wi<(Zy zov<+dEWx}(O|Ng7==gS@ZXumwb_hmm6OK%{uY-00j*POQ#Rtibgsp<$Kp+f2guleW zTLM1{VLpODh)ZwtQ+J3;zeXOSafBiBlW)aLRDZ+w8+-@@iD7@3F9q$d{PX@P3?!r6 zQa^pS_`vpwrruf1JXp-WIiG%dK79v4zllA47x~PWvG;))Kw$cWLez(|+=J}D4j3T? zZV5SpUqTd;H8>(kgQ}4SD`I45b3je4G=Vae-a>Ua1VhJ*d~oESF?r;ZL=Y=dpJXwC zp@JiG86nZs3`#pUI3ZCyWFQ)a4#iHcI6_wKxQZ}^?w279vSB`$dl4yWoCv^4zzeGp z?elJNnpkIEG0otGXsz(Coz1&gsJO@7b;!2!D3&5sbxM9^Q$j8Jm=uiRXx?*ywSg0a z(&+c5DZ_cK5=R;xL_GolYosoZU#Q3+4i^bQoyOq_LPdMNN$(@k9Yo4R9-E0cf;iZ1 z!pt8z9<~iP?6@xxb9JWq^`Sp=fzevgs^yV3M7b7#^-0fD;fc({R=Ojn439D)GXzdL zQmYP{InwDuw>qNd0BH=1cR;uev@Srd4}Lg;>H>dWc(U$*wqbApB!w4a=;rsTgTTxyv?f4oA<#?X=x-vAzM z6>cC_S^mgf%HXk>s|`i z3kfHTf2H@*iQ<`E|EWafqz|7{8)sqR1|dQ)4ll(+wurfMH~!?BlVP{NZ-o`?nw>?* zvnRI55a(tISI>l|+xcWb`X~Iv=+p$GJxoUZK=~JK%1U+;0Tn33+|J*39o#o0?ZL#} zz`xIN!DXwbsHd*U({qF1(@s@Q>542!u^~%PsHM{51JSFA2Bw%~6UEMY1m29OCXes)w7AE(C_sm&?68rY#JZY6VCuVf<;xO-@Q#Wsiw2a zLw@-NS(_zPt929FpAQo;@ z1O=x-A!w6uSw`*`kpGu++b-(Z9y8&Fj7KLAM({(MVI|97#w?c)0HRofKoGWs>e9P+ zq{mKYAes-7+LducQQbGqK9S{Q#k4njWzt)sWogqTmde^sihCzd#tZQ`&0#=Rqd41@ zD(o0v*D)476u1oa<&}{(b-;usvV^XiN7`Xfaip6{y5_WTY-6_!t}&G5J|vG-Li$Z7!>8hFyFW3iVQM?C4kgJV^Yxs#oPz!0JR}chFjdRg2}>GhIu!L zYK8i3dHqFCq6@YwJ0W5GGf4^@%ttfVAry&X2IoO&246DYM`hCwjFb#y#sbYEeu!r@ zmyV(A28I%hG+E9oSrG@x$q)$Lk;2gfhjHMf8AmBZ8|!)~jk}v|=Q0nP{)vx5ue<_o z6td^8*|XzrCj2nXc4x3QmdC`|7B?Za#gXQ;qsi0VxSRQXJ$S92^r6F@N|7(Fqa~ta zRZz>eq`tPOzBX*d)`p?bZfCaJ(e~(&9fuP+_#=DH3+5djFh%yth>7)6x~WVOC2@yJ z773dud@n}t+7}(ZkAk3hyv}gaUa23x&!SvEEjLo~CuWmWSYD9Y?kCO-Q>w%yva)*1*m;HUq z`N0CJd{2lSm_E39Jh_*G{7e665?1}*jeYt2C)Z_-6>&gm&8=FxJgKJ6b@6MmO~^{} zdR8uPGUm1G;JfMK`3AuagE5L2#f8&D2~zNN0OdN=ZbRY~qm5f-*Qtz$TR$^RIXW4} zt&GQ&FcEiclDwH!ZC`gelOeE_*+7HD)8^nfX?it^!7XpRSB9rKy@UJ*t za5fb98Ai0!`8sJ!(wBV!pEcAg>{NC63jOmgR)sH|zz9fpB^bdeuvUqwJ9RSZb-A9n z5zd&Qz-=loELKEqE`6F6#}o$TY>*pr!M#&{CRTE*(wEUHdoqT+cYiM62Q4_^_rTiP zC1EcnN^b6@p;UYc{V0@UnVNlF!bQc;R#x9w-mQ6-AQ16vzJG?}uifmMq9-v7M*UR& zmtXj3EdBzPyrU=!KjCESFiEDQBn?#tdQ%}8;bd%_ZAOSd_+;}V<=;at&!(f}%dg3H zUirNLIBq}3-T0IWt~opDf*<^0aN4=9nKkdH z!gtAgecIb(4W8x{S zfsP#M@)XeRgFqZnaG1dHJGL<_dFUC}tUMd#xrDh>CQilhQwapB*(MEOgo9Ececs>( z$3y5hus;RY+kTyLM7Xi;`f<*(pm1iJT9blNKKA3A5r0AwH?|&fOlYNO>3L|W^Su+A z^brZOvKY$e(88g(j+iWwohTFuCdSz#fE|?tCXQSWG4_KnM#D#e0j#Qt&7d%yrSHH& zojCP_h*N7O?_7R+c-}buhHZU82C20M`-~47|KqC0zx?35MK~a!O>!V0<^RtkrT><5 zt2nqinV5-MIhmQbI5>Fo>9Ot<}=iwhdt>#diC)wY9aatyKm$1KZZm zF#fkbP8oA43XFcgft}A={%!uV{4+DJrhr#%_&j(;1#ldhN%Yj(e_XbNr5_0@q_}tHpyHqt0zq-WO#IP%Optf*{XVcD&O-rW z{^(gq$UN*EW5gaN_@@X=pK7?x_d5##!ccTQ^rZy4&u+ZKJ~Q~=XA^J&jr&?2ADx055hf^2jhb(1{FeE%6%-y=R@-HqxTrU-pTz+_t#OFDO2lu4pzlsTE1FW(jvji6%3vc8x4g7e zj*>D+nMNnFgoIWil$|4MIIqG^deIk3c645%dqu0}*C|e_OG0a86b#B^TGnCiM#hVLzyQma9 zWM+=%)OI$bEpfpeD{ZiAJ=1(?P4wKuKFwtgSe<-*mmC*kx#4d^I4|r$b9W43x~iEk zyZPr1(fAtSOn40lmqOm!rU|JtQ)=v-%8~Z{Z|K*gjvvw0b1t5(#g*=^DxL4{q}x9_ z4Vx}fD+C;o%$8H2>lBzQPeJFdD>}jLJ(fPu(W=Ke(j7%3-{fTH$DGAgmq$}MY$Uy) zcBvB5{Edq53o29;X?1#J9kNMowlb|G%4kJllgPkZRWcdUIGlAQkev{4RMLD;ZA!GA z+iPJ`?9B}L{WeTv739lLJNmA637JW-g8g_{Pb9Bt;dHd&k zqh^837VrRN`A}o_mBV;ozU6}bO`xBW!}<59=ZzuMa%Z^|(@?~Q5-;sXRFWg@x0LXl$k8Pqb zL28fcFmFt=wP+zV&FY~b->9!xDNj~aQv4T9r7B^86@2RoQbB<9GIhlQT&npJU8?yq zHB4^R5?)sIg4K}HJkqH`gdv8T7Z9VG6+v#{QYM1r!@ zquF9d8-p6|z`_H1>EROvE1{ukHYn2MU z(kH80Vay5_VsKGqw+J-t@+Gg!ebVMFyX68!ivSKNOk33pk}Wdb?b0Q4?dqk`m0HF4 z3>|T<%2olRVJT|7_W;JEA+{ zW%q{%nyLK!FyTJu1@zZP*2y-m76WMXsZmdl^|Z*J{dy}j>YGP@rz}#tLjXzfvX8>k znSPS(`14vrqvU&3pdf1o)3*z9b<8?bdl0my(&jbI$yysTwlw6XQ3Yxc+`T6Zcc=y; zv>NRyo}0T{gQj7jj5K|Beng_*fC#FSgq;bsuuL!y47wBR{m8~N_49XTfk zkkz3U%(>Z$RUQ&F#MG$6`0`8hy-8uThSk??b*@dU>)Xj2w#m{swi^A_#c_h8mt6ie z)a*?amS`cmnOj*#mlr|pkE!#)vGd*!ZiJ{_)sjVKv|(B&k7o#h-+n`trGMu5Vb39- zsSFfNA@VepBAGkan<}|CP)^l-=1gK)r!t1Fnf34<)bpu{n!2Ng2Yg!-(8Z>FHR4jn z?1DIC#c%V*-y5rGd?;VC|jUFt|kE<~ud0gx~Nj|YNUR53U^de#vn9^Kd zfH(D?KTKBC^gK1uqhuTZgi~zKRxBdlY%wft07p2z6+yL{bd$mM*>trOu7mwA;0$t< zZ(K^)>8@736AiYBTJ<>nHO?gZwLwkYj;?AG^s*}qQDVerUK&}6OV>v7ifOR&XU>2R zGEFchl@DkYm z$n%;_H{yuPuTy@6zuQ4F9d~DqwddiO+wZh61JM`S` zPj|Mm2Y=0Eu?JP@#@T8H;986!2s8^eKQ80wDvZ z-wo)K?@rSkx$6uej5fe`VARF1*vWmUETR<<>dm9qB)^;Sn{xcb>heqKsCAj8J2ehz z+y~RqrfO52dcmExwQfJqk9mH2vOcSH^RX_bZ7xs80BvKzpb=d-1|y{U_7)-8@yv)` zL!=ctJW-VdXf_7%Jg*rG1WuYxTNXqRxua`Dr5X5>Q_&K=Xk+f^Fi$U7 zxF2+k+*wj5;DapE`_%WgaNh;m+KD<=n^A&mSJ8!LJhnLAS$IP68<7e@gx|UXFSM7p zrvP`f<##@T8AF~JYQ%%*Bg+j}w7~smWUx^tPSOkjFYdroamIz1d-E`7G*i@O2;Dd} zg2XJ9Jh`JFmqkp3(##9fuoc0z%e%sv&JOboC}Q4$kw&vCcd$5sA`qVth3<*p11Ppv zxJ`9;$q5b?Y+D%dc94YNad(6`xoY0Huvqh&>fo+y$RVva1nD@il%OF2gV;0^()evf zvc66tz`wnT@RK=LLNuh_B7biP&=t!$0M-7+dQWJ!z&Ce;idR9oxvubZ%NGbXE5y>j zY*~jJE9Vh&hKd)jb8nm>dt^l2ooGDZ>3r^Zy41-XeP)|yBR2bd)__*xDE&Y+LACLQ zH$Ul6|1|5burCk(iH@hZfOQ`kb0=Jp%uLdNVp-RTnW(qt_)C@6Eb_=*tZqC`w;=;; zg=>1J-4?A$LP%CO0#+~Y*b+uK5|yn;=m$hAz9`M*f=BCa_Rt_5;tysKZ`c|fP^{_f zZ;vUL+)Xi+7uQ%03Cpw-m40xtvz3%q)@&^>v7_6ZB?XpLV1RQfKG2}5y+&* zL*S7L$9&KgAdwS$E374S;lx^bEz~7T{;MEr}vz3LCiw|B|z}^n6 zv#UzvRX=*QZ*=1W)4lZuFccNGw*`PK?+6CLy0vi)^eSE#y2|K?H|_yf6w_02^C-<{ zdNNSXf8^voRrWHMcPd?yZlZE{m@fu4V+npR{@)uYh8v4Y-~TjFt|UM}V*kGzsQ=<& zS-DvKHw$Z33(^B^9r@?%wj}iygA5Ex6ceguiZ}{bN)n2Azk$qDY63WXf7QyBF6TC7 zmmf~vcsz1BhYOH1A|(m9Ml0uv#03e2y@xiD>CQzGv@bg`*u6* z^?X>I%4zZco5>6Zswrn+JUu8AZrP7Le53$$Y7J%gP7Ko@9?+xgX%Cf9`gmaO-HdwQ z>qEDH5FPmn?8Nbs_52M-+z%E&+ZPG#KqX`VK42geh*NmKm)fB}Ab@`R=SX<_c0eZH zOEpS8_|am>LU9m-84XBAw0Hun($EK@X>@Z`V|#g@WmL`KIxv7(J#(G@}0cUQZ$}m6++E79EGj;A3#`5 zLKX2tq>BYh{d+3nl(78Xy*!Bng)EP^20~8Q_a6>}7u)xls}3KI@XBw3rX9i>i7VOeSN<+ck| zenI8*JQXm$Hps2C9DD_rzQS(8QW9(xA)n}asPl6&@t%?D)8!>fO2bsCwa^~9z7c} z6m~mraO5r)wEX*MG>HjC zqq0%@(pkv|ilLGZRG-0+d{lK=W05g}nbX=3JUui`3-^NJT^<8^6~Sz&EAa5~tems3Ya4v{cO6JL z6^mVN;7aCTzPghY*KV|7ttN{RWjB*2vFC16wjuEJD{Dzs&4iMpRovJMA=mD*YI*bX z38VpYTU40gEp2#KQ2+W$n?%Jhs#+l#;YN!>=DxTFTavkJ7G!}g?AdrN+D%Tos_YeU)H|Y{>$T{z$7rfFrahmySS?U;Bg-v6ufpYg z4B;veJWg*pa*q(!Hhl1k{oiv*d4kInlVn9XPfctBEL9pOtx98hBNc?d(z6OuKja`j z6Hebj56Yw#=d-0(qV@|qFGy!$hh#1IctN6e9BAsoffEH@F){=YDp=tX6_TV5Q_sJr z;?Vshs=PEAOvM^2sB!#^0@-Tmd#@%!tFM&M{drM4Hc+1|$9eWkbyE|Ix1=oO_b^_ac@L7T?DJ- zG>KLmvq@HQ1=*E%Ho{$SmR__j4Igl36_Pj(Uy&}GM1I1)21}zo|$ROFp)GJ zKsnO)M7BBR1!-2~wMpkFnKYHsMb??tU~$@2N9W2K<<&-N%&M8{$p&TYEkUIvUXg24 zEpcnpErAT^#jz}QMYAaE6DyK(EOv#nEN~gvKenlC^z)6Ci4|I97=;&dDzZBOjF8OA z#U$M6kkvy#xJ~ucAGQAUHf35PH8Y8>`4Ulvvs6s_me_>yn3xc5S-M(T1R{~f2sM{? zcdp>E6qC`j+-wk|+Kftwu#Fk(5Hp+UrYG=BbL8l6PMzlMGN^l2j{{K6fo5B2!L0dQQ44PbKZ&syR1JuFNaL?O~-i> z6m2qdTPXVQL>*sX!Rj#|&p;Y-?FTp#6@bUzM~VaK!b?`$Ldvk5^YyQv{WiV3?#lXB zb93+A{_W3P@b?XWZy>)Oq|WC*FOcomSHP=gW@2@-y`V@+8q zHG8qI#|WQ&sy<$#y4)Nkcbm0%Hp(VNi_u8fXt~H@ee2kaA#dktoU;mv%t9NM zQUmZJc4n_1$7XJ7hT!E%Gwn0fWGEiMpR)ZZag}NR#8lXgx<^)-8C|0g;(Rg}Sz8sQ zY$&1GRfyU-uNrMIZ(~?EWduY>btfYgUZ*LF4n_*Gce||5j!E{4=u3@gr5Xd4o20TV z*I(gI?yc1G#uw@ubp;Y$8pu{F-EhbG;$co;^H5XC9`62t+uDi1Lk+4NJ3@uvl~#g~ zk@!=Swkm|5u$OTVRaG2aSdsOu2eFAo1rW{~hMKbp=qIcF+o{I2t97RdFZRljQqo2< z_JmXmp8g*6NZhk|lsaf?)GNbp(HW~23x9&?Q65{}4{qce6pF@+B~bdtS1+EdO=Mxq z6ZaX8q>SX@;&Kr!mN52dI{6bKQqtN^1xKI&adO|B{lC?ACGb#gZG1`+*%Cq#MaayM zC5j~3#$+!`wr0l2GBM4VL1f7gBW`3*T1graCE3D6BV#Fh$j!dAQIxdY(Q-BK)!e?i z-^}m*&6)Si^Z!3*dCz&DdCz$silZ@Um#EN8HKy+}4lI~>|MjM45XG{h&3Nm&PILmOPi%YC9NrEALOiDKp)Yd;Pd=&TAx{-*Y6` z9MX}3*6hLD#bBx}F$R21d%NE^M{8G#an91jIO`nRjcDVwP7+O6l_h>$?*&FjoV8Cm zSNT@2%x_xt8WyCmRofiT%kJ3rW_OMzbq>Ytq;5*}<;8_vrdw%7h(aBzUS({d4;opj zMj+QTp3j!b^WyFq>xmY~Ahab6CHgn!57H5_(Te!qV%>8-_HxOE86gl#pDP%7D9^=F zJ^x&C;mHqy!BQ*1&0D*5YaU2c206H&-%+W`A#z9}%x@L1a-TkZV6z~cya=$Mu| zEg>_>bXocRji)^#uaJmbj&y!4_IoF#T!U7mT}sJ;!Xs9b4*&0Psrt69csI!y<-Y zm+2L#&iLCI;n7NGg`ToooKTVtH6&)w4AgM0T|aD}5$B9+(ATt zXj{S`7sjE(qcB{zllImCi<(Uobdv}25OJ#f9uDT|TOXKjP_5$BnkGi_7H>9p8fCFDZVlx;3LVtJKrr`mCy$ z!GqDVNQIi2sSlV)h3lj<81cC=>m=<;xfzxBh#A`5)C%EyUGS>bO)XB;l%j%9cSxwn zXd6sreM+_X8@JQrB^qt@;^9J7(}L}=Y=5!SG7e@GNmEHJ`LY9RRQQQ6tvgyX3{GvN zi2F@JdcDmGlGME*-aY%B?(J8%mE_DreoW8AupNLjt;ji6*jeq>mfl$2)$e708GGzLnD+F4H`2wgV6ycTl2V`Z6Qc0%GaqmC3%rB2b=T+TP{h zdYWmOLOJSw6EZe>BJ0?6ZG9FI3iZ39^yyUiRR}z@5Pr?>n0$EpN62RG$0pB=D33R# z!l*0E-To|Hp0>WIIq2vJdVA&SA8*+bVH_skOpZQ|`hz@Wc5dSYIBA+OLq4Ot}}h?qsYnvY3lZPS?!fu)pXdFHIB6)5)Ds zbI{~`msS;~rIU#vG>DX?Rr~3!r3&IDI4|Ic5wcjaKQ7%e1r46gkMptKs#n$&$v@n; zv^L*3LG0m-;YZ#h9#d}c8&hCz+w5i+&m^wR6S{kGk)gRp8GDV|a;D;4lKcx&CkIDE zRQGFJZ1v|M<}~Pdy6RQvMH{LHElroazdd?G=tVfHTdj6?uGC;*;HUoQ`N8rCh@~I|pd!7GNvfOGn+1agBbl>7)*xHH3We28=ULk%z{m z_QSjq9BmHiW;^Lr6?y?@Zj6G3S$3ZVg>b; zH`J3`#l?c7a3sTh8yx#dLN;NIXYPA4|5~dcq7bt#u!az%C;-=D-T>XO<6zAmO(^J>Fpn(UU5ogXQoK1lA%D=}}Sj?=|9-sX% zUD}fDJNoXl+?he^v(KT1{kM(qc)W&;ldB((fu*HGz>{m_5n+>uuXVr^yunaw@24$k z0*WCo?8j;GN^I-KZOjfld)HdrIc78Yuq$z>z2Q~qj?!VxqIz*Duiag1;_Sm-2T9e# zI|BTO-LgA$mqsLiI_$K)mVIPxPsz|^p%K0#VBr0&ifcy#H|7QS5W5fBal(VtUlUu< z{<61v3)|m@O1A4C_lr5p^JdSG6^GNCw6O;>w};L=&k&_VMDaOyzu(WJUA}yz;tHH2 zsTJOb2~K{lqBca>ISii(IjHome)5T;Ief`dLAJ$5Bu>z`7eDCP=d8a+8=%0Vo6LH$tnT?giN+yX4=DCoSARm+9*gq#x zR>EtTghiJ8a0Ui~2;B`3%VwmWg1!ur3)N-zV~}|l9J-8=^NSk}=`CMoZy@yOo6}=1 zj0%)xwBCLuXx*hh3$4qzNAN}9Ty}dOVhqsk$oc<2{mlqISc1>r>qvlZK!AD>3w(4_ zIY6Mu)gX`slO7TrKdZ-=r7Kow9|oDL`#}N@G0(RfhyMI&i9Sp~<9!yVhtEn|>;!{A zqGS+Afr)+--%rvn4(wol!wkfj-^h$oY+(YvrpXd`aqlTW_`p6l!2=BVZH(qqVuJsm z{S)wjDA24ruOw6Fy{h}~u3-M5t7)&X2csGyR)IiTOuA0|1lo5ItiSGlx5A=Pi|aj( zG}+_CV44>GR*G*-dB(pGEBoIXp_TnfyvK47=pv(F2qrp=%mi{iw5{>u95I*;3 zZ$>7b@e`E);_3ingD<7Wi|@{-dXWKuVw~zCcN?`&Gl7RgVy1>Jj1he{9JDQGgckf~Msx zQ2xapW~HhCldACg#f(;G0%?K9>jJF={eLuN&Ef3J=S9~u-gb=J z)9$AEZ+`($O9KQH000OG0000%06@;M`eH%=07gpy06zc#0B3SxWNc+EI4&_TFKlUI zFK}#iXK8M8FK2RLWNc+EaBOvFX>M~Zc4cyNX>V>VV_|e*Y;R{RI4&_TE^1+Nl)D44 zC`-02x@_CFZR};+wr$(CZDTLnwr$(SUjMw;{eSf7`|rKc88M?Wvc}BH95bpi#;ijj2N3;JjsDH&k{}W^8Xkct@@;^f1{|+^< zxA-67=>HXNZ|!1cVe9liy5ax#Zbk;q2G(}}bv^`55m@Aa005VN8TbG0F7%J9{Qqqh zad$Scb+WLtr8BZNaB_-Kn3NP^M969CuBoZJ7zp|EOUIn8BR*2Tq?QT|YCEo-Q8r?n zl)V*&e>wzzPb?xy6(K4xAbu*F*=&a77QfC8z#i zsxHGm6MaqgpF1a=glU&EJ=#rtid(#K`=tHC`eF(=8p5o$vlvqjF^hRj&WM2Q^B?jg zVPyCDEEV)Cj3|)hkBdrmB z|Cpp%aV9tbfEpSA0M7rNe^nF5|3moS!tf9Fhc(^wu~$8Qd`QZHBa(!LQl-ZerD?N> z#QRrRNJZEn0#Qr?iy;#mgvp-}c>>7+-57524Y*s9+K4T<7D!w&b7q{$ZHZDQ$~!k% za?W?WZh04dZ*T0?!4Z?IkKhk>wm)Bd~N5$@F4MslAA$CJ<(YSYs_q_%QzJ5XA4~_=8a|Ns21sR?q z6M7@9l4!#pgmKk_QWI|Vl?|Bpn}NKT32_AC!yTwV@*|nv;luBf-z5orDTBKnlm)$E z-hXAky=wrzza#j<@#7EC2j4}#l_2LGabA`KXPSwi%N>Y>Z`67I7Hz0pGU((bzn zzUh4B2JfcbfeR+^qwxEo4B#Ki1-tuU4Q&d&F@Jer0Dh@J_{H=hdih}w?C#$Ny@B69 z#2uXG3BH+qNnzN$x!r%@UvtF#prHJb8t?*>%7OV@ zt-f7;DTtvMkrj)>^wO%s;jKW;bXBy5N`{aG)rLk%0p#18QUB)Zj9}vCsfTq#(Zy+V z&ELt@2@x}upi0b7ZI4_Qw+(GM63d!wXyhq@iAuDggw{D!7pbXAW~@p^XpB1SG{c74 zJDb*Chmy*R^yL=RT$cRl(pibKX6vS}E0nw%4AvM08Od(CYgaLS>EDH!YR@7na#oe& zzPbxc!-3Oz@?tua>KjO#GtsI7Oc}1vktG+UF>Gy_4lFq;t0~q?ta63H+!wYGftV_4 z8C?nlR~4k@lqVw0T+_YPW280;vaEiIMail2s8#Cv#J!iY=3ZX5h@~B~4pZ`n1bJGWd!JdeQ892o z362cCa|~v7{5BULk21+}ejcQ4+uB-rt~S+BmBFuD0xq^f%AKw1Ey1lqBhUrH?R`B< zO(QWtjWaT>iiPa2$}rH#(7LI6{akznhhwa1CNDC$2LI0 zWzE1=LZm~Nk7abfuYZ+OXOwuOKi3nRpg>!wB*WsJeu2#b2gXD(mxm#WA=%nVuE zYKs-fc8$TeBh!yJ<+6!4DU`}~NR=|!5M61q=3%_w$ZpWYw~9N*N$WJJ6xuLtOKqBL z2yl~j$d%e|h`1L6GR`WRtre^EpUsF!;7z|T4K21}6t0mE+;oy|5HIR(j3RB(ukjNo z!C$9Uq!DGi-;`H%tb}J2!Xkj#wp&>zm)9kIfR+IxiI{lCbEs{iMQXY#lm-o@FdP6? znYJ$Q@+RP1>Ka0AONj>T>fAbaW*c)!hZ%DTikWbU!b`CbEIJ&_ZC(StqzO-0LZ1M> z7J2j;wv%vCE?%A`3&)K;Q4AYn6}L$XCC8hqdt)Y?v&1|gbVRnOAS%zplsH>N+jNw{ zR!B)QTD^yug6_|393`JcOUKP)pzmy6=Vk-`!K4-H4~#aus}`2X7_krp87~%&n`9B8 zjLkcj%|wjNNja;QmYd_um|4sgmYaC;^aL&ws8*)2G1Hu^%;P>a@r*=?Kr@!y)M#Fl zs3hnlQ3_2pSvU)nMvoxf-fpY>?P)o}*A4!tF>L6|8R00Xs6!-F%EL>m6hL=}vy(m+ zU12-~*hQAgLGNuv`1+2g;Bq@ruBrm$2xVq?Euc)?HY-4jR0Ldw$^36JD~dG>_~yA%TO&IojcADmsgG9(B<7=_vPIvtng1 z1@=+1@4nVAa23odW@qQ~lnNCEtdxrz3{H=DQ}?J9RMBXEda^U3+tbTw(&nzcQf=a3 zAmrY)pmI+mS0l}KPR9pP#8;p++CF}n`0P6D_`0?` zdhzXpJWmBax=~Pt$x@p&abOsvwCQ0h&X<{s{(CW9!^j45GehItrcM9I(pCq|?)^;B z@uzw$V3Oy4MQc8)&#P;4x~ifnL2Z(ha9LPF-HQu+eX!6plZe&M5mhl~Obwz8i_9^J z9Ts0F#s4dY8JuE2g?C852=OW#d=!hgQ}eLyHkx= z4xGVh7>GZGv&9T$Vc?dRdl zUKtzpE~_Z{ZUXJCE@;nz7Oq^~AF?r=j2(*DPfe`HtL z$(Fm>0c2wpXM1ZXzZMj`)L* zg(KWNQhu-nuB8uvML6cAWvYxg=oT_Ho@$2iafWvI2p5x?l6 z1=}C8gzZjj8BHE~a+bDj?Ek)vz{7PPKvaRGn3aw=M~Gx=NG@BNvZrY`AFb zVmo_%*=vm(DPy{c=CwFHvlhbYA5ZK9bj(Ku5XcbvRHY9bbmaie`{8`$RJy|6eL4=h zgL(7V%7dfzL%8o((`=jYw#v4#IKDrSgg!As+bg+5|m7j+8-ZQ| zW1QY%zkQB~|G=IM;on*gukXm^lj7+1m!jdoGYoBe3&U3HJ3-Ib!an&yY0ZS(8lZNn z);FV?@U}8C?;)mIqI_TN9$sxPUiCg+^)}Qx<<>gU)b?wJr_5D_FdmLIuGB<#LdzA& z7gPbKm{1g0c}3M!jrf(;G!FZTXghwh43GpTvA27M+0{E&Z+F+crK4kCT30foGX5S< zvPq)WO3|S|jI>(S5Jd}E9)4I(U!GU16&(mMPEjMB^V?KAa=ONRnZ5)SI`!w>vq$4|2jY`Gzu7;{!kXJ~*X4wsFaDZ;X3qjB9U#T6KCSiY6xB-LstZ!I{YU%=KCIcO5aN!c0PnX%^Tn@Za{m}DA8 zJ`Kk%Lq$^tl*6ccDw6g?*vi)wb;RQcD>jK{-?eDt!)DfMC!w2N&G~__`$StBD$9F*dZge&KJ5qVmIw`c3mH^vV<1OQli( zaWh#^2oGGFF|sxkI6;kJs!MDwyKJzCnqzT-(aRGw&Y}6Jo6(IP6VL56aYomM80#Qh z);<u^CM|if8xhG{;&SA_4Tj|@?8@y*x z#|OV>SI1}L^QGvG(d`-iP0H=L?ydC){J+m5Q2g&g)WX);#L?Ek`oAO;h*BSb5B{p< z00IDr{s;a1Q_}u_IR%M--uP#3A--P{m=PiLr^0Hr2I>Q#+hZ=O{j8z{Whs!LN&fD{ zU@Ml8l!SB|EpIvqeorXEqVvi?;0fW)jOo3HP;VDM55U$wvncJ5V-#{iFC9~gRjry9p11FIlk50b0WJmMl1X86gJy!bF5oCoPqV!VjAw6 z(mE+h;{6wROW3UI4cG=L0_O!zaXmRq!a;+|j*1HcxpVWhNc_LExM^cad;$sp(1id1 zp!grKC~RVC;9~9k|Cc8Cw`@&~nw1)g7(#b9JXjiHYD|9mkg!MyD2|LYkp>IUUs6I? zjnO>X!ZfSd#nm(iJkTR+bys300@72K=RG@VH#zBICj>Ng6zPjeX;DST<MEqcF5NBr;*WyFCKICLCMdzh6q)vn)VOWXj8 zT`7DY8F&%QmJx$F!5M0ReA8i;UQ;9w9mn9lp=m-^IoVc!o#!Z4t*ICrYWFzSMK?J_ z3!#=)gyAdv(@^p);Av|DYvVSU zh|hv-(xQCP?EzLqcUcsqrmnlwV%ort_hyTtTyE;9gxM6ITDM3-MoDqZjgb;+vciSv zyShirFj1z%-02+38>wdgjm}2f)l9kG-Q*cuOpHv#Hgt9E)t%QQx)E%et z)J?!iP%pG>;vKDUCN-(_(%?2hUk*J*O9R7Qmu)GGcAFV_S~9;2;Qyzw*0l zv2$N?rDA`_{s&x#ZckQ2v!#kBQs4IrifST&89rWt=g+{-bQQ+nj6X@jC*Ah zu(GyaG*LmxXPsI&7K6A{oDlV;c%lGka5;3Qts`WKsM#wJA~mf*a<}+T9n&51U_$81u)V$$ zH%{>lW?5c`YvpA$SJqh>eNij{)+GsNXt#J`7rc&fYo4fXUq0fV=L>wjwH{fj?FaJx zxjR`xo-7SfMxN}2nUbFG28Meq83F(HR>~NE5aOs_IIoCP3;!o}W*PeL%M+(!e>3xl zeab04mm#%t5C1{{vG_g(!9}42FNeS*oLFLb=(_V=)fnKqB-W-UupN?WNv?3ABMFJH zd6}^k=in1^`{<(=kc`0SUzVs~C_o@7wpdT5V}*4cTi8dUoR;TG@p%SrFi;rJ^*6D3K&Z@X8i^0zu^WXcANxv{3Q+ZdFhl{Pp20 zJ-hR&MP*2DEUrfK;4`Y#X~3;Wk(YCwhsFSk0bt94yToQGb5J>% z>BVy=^pl8LPkWfSH4VweteT)d`dlRw=`0fxe#;Uxohw4SQ1Fc1oRL$6-!5Rg=go}X zLHKvHC0phxOOM7*fav!IDw%yLKmWEi(_5Lw>cIm5^icr-DE~*RioJ=gv5Bpbg^81Z zn}MUr-`5vG7Yl1+6UTq7E7KZ~?%K;Pf8tm&*)u&FO!jRM()&|wJV-q5pi*&zXgm~X z)*DQOqxuEa)i=#G6Ef$<5UP;e; zifuNWi&&m|U#H2Y%`q^0eBb##yP4fMFa2h^PtT)v+rOy%r;#2H2z*!iW<3;woSx4{ zJ)Yeqc=;{|C*4JO=`h;PM0jUBIK3r!OP6~lBRdN5`0e(bz;vGt5obK^Jf3~0gXrDw zpz*#6Kc1y@Sq{=J9Z-7@hU*SiyWWU=w}a>_rhE4X)9z8}1K(uu`GV^{?gYM4J-=E4 zcJB*7d#mvD9uHpcKKXb8`0w`N@5R9Wv_+747eNNvQLy<{IRBxB|FaR++rNwT#*NRm zce^~rU;h154&uES_eTHx zX4m}TBJv$qxFf&x^Kpmkt;D;-0)m59pr}~1MvAgM{<2 z{5=ELv2ch8W~AQ|EX*Q=Q!HpeZG(th5Z*Ez8R{9iH}{;M(iC=O9NEyB6Vl0)a25_0 zOKmD+UO{S$ia{x(U@yh`hZsuEq-4lw%Tgh0izH*XnV7Ya3TN8HLtGBGMd{vL^xbw& zHa9Z(T6yL)5SuzwxZb&xzYK{pZ$~vmhI^vOxwx{Xv3hE>6d-CRB0^fzf|+I0PIJm9 zMY<}~PYx8G>wLwoZ1j*AST&Ji?0`PLu*H0kxvAf{>`q!&eE z=B$LJ{eVM)C!?gxAe9d2rsnE&g>@~^(^`=#S!Qm=O=W}Dyt#uy zxjPNY6>d`s&}0<59O{-F9d^~e;@bY5Q;9V<+oQDund=lz#;ix--WEf8%K*6tf{Bv1 z0T*ZUD%k{)flof9YpRgj`54V_oqg}Gi;={8>A*0zmL*uSt9t!)9PYLqN08Svz28Me zfe|T|LLW95jxmBM@36n!9RtaDjq&GBM>3f#tEt^mwbNw2GBB>Hv4R`(3!N(>7|1tH z01*RP?Moq4)rh@13Gsw3VDGUGkfU{q1|EeP8f+yva?`2mr@(~o>{YOm2M4qYi=a@< zd-I46Hcxl7JEF>dDzg7LO4grakvzsGhRj8OEGngNo1*12sXv51QiBw{|1P$KRwsyB zVj4o>xocs~;x|X5m~%$YVA2+dP{h|GCeSV#e*@9`!aWV4&gOl(+8&+`-iw9mP@c!5 zkeF7mGISrZq!baHBn#k`i_@#>Mr~T7XfrsmcIdZSm-qNU3osMo$h>y3)M8}n{WCf9(?AxpFN=8HD~}k&U@;D$gf5v+7Fkek zh)`#_7jmB(V&tUlSWFurJf z{n7Vp$@CCw%!omM7^uCJZBo%j`CB<*=(&_8>3G7(SE!b~M8lThNK{F6OePdWx?Mwc z;oWTng$(H2U0+&2UPq9%09bb`&q@z6TX(OiuuSJhvdBkglZu+`cw8P~hJ zO2IYLv2#nHV{+*A;8PlAFMh8yH`+0wAsjP3dqEThbSPe@)HzD8-axj-O^!=VfjlM3 z<}6{Gj4whiqd`ogQH|3{1Ffx*^!F4v8B>L`|LZ{?!*YgraT(0InPeT(E@K@?S;npE z%MJNFdss@yfE@Lw3}?PFPdOg&0sC=$Asn@(d@P1z8KsaTdL8qqBy=+6Z9-A$7VW6C zH;Nn-Tvy|E2jOeFV;#;Z>w9L0zjp&GYC|V`jXlokUgpIOYZuTAM-j+nCEZ1!)`=Gp z6yN>!*RUAsSvxNKCAaos`gD=KDmog?_4&!oV5XTv`g){IZS3-usWl@r!E|0l&zSv! zq^ybDCF!y4%Y{;QihOo@q>9crv|c99xqGcJtu5^)aWbj|{H-^28+kPed={ofF+bi- zV-X~1XJ~EDPHq1uSVt_*y3ZScTvlN|*)^jV6N`Qc>g;tpbER{no4V@zUE4uxh5G!B z12v~Pe|&*9_o;v3_4iDvWA>-hS3|ba$5dHdOgS>tO2$F{5%Stq3}Vl=0(D96JfEfx zy*imDX>3d}ab@RXIBZODVL=0FaY?kQb@C1Jl^-}KR4sk`9IDHK=z^?p4TGJClvZcw zB+vx%cv@>o&2YhZ3h#18v{nW1i?OB;)N)NeaTse|jsM+Zrt(~N_(?AmxQ**9jP=8tk)$e|pzR;H@NhYIJ*C{(s zy8PY&!r#D_xK>5amVB*j$0=pr3y>zUpccZo@p-iIqv8xDgiy6hb85qNUBVpCS{)NW z4Yuz$l#~34t=t${4=CofB#(M+L-riY;IpRIKQ`02%rrf6z~=^B_MOEkFk85f7WvOja1~VKF52 zgXnt3g03elDvch8M9%9^BJ-8xo!MI?^F_)-tEQnqipYt~YZCJ5h%yN?h~Yvf4X`V` zn~X32_%z}1RYaYlN7YI+*<&I9tj1uKY387&E_;lQxq#MyW*~jh4a>%x zque0E6KAs6oFC}(Q|Rlj`kF%`?|xUlY5kgqNfDOp#hzX@tE2tKu$S8rqN@V67emzV z-*DX!2K+wDWxOayA0p}jEKJIUm{AT;@QULq42%(!Hrp-LKgsaYwT>rv2V1Xa6yo9Z zuDc`_A=+Z86E1PBdIN#7YIC<8{vl<*pP{hgbo+@ccbA;EL1|PEqq0)(=1ap%yql;E zG4avszlgr_{2*7WRHo7G2OG`lP3?_JY*5xmexu?pwpD@)n@#UBt9HAWZU z{G~377$1iiKS7v6i$7B)53DV7 zx6jbC>e&TKMOWkK!1ieI%;Gx341)>$!Hh!S+&wjZR~6*_)G_|nnV^>!{2dqWNNR3J z2kx33rRbY!Cq>Lt3GF=1!jy8tt}giD6PjXIGN`{}K{~soKGeCM&DzA~%+Q5ns0)us z^l<3f>%2z{B87SAJ?hOKZ$GSUP{1A9?Zp0D9&J_2V}qmI?qtX8-iQms>=A-LDvmk1 zuwos8Ukh*FNF$g(P00P!I+&h9@Xg5t;m=&sJl9bQYb`+fj77UtyZ3> z5StlzSm{Ka;X@ru?=-wyI=oxeLtansR-#vkX**jd-xXikm|GQv9rlh2i;t3bUHuCjWR(T_xRt~H0-si8DH?vAf=uIrAL<{k8Izh43<`;-L;u;!pkIxWu zXDY8dSEProved488{K+yBbo~FM>tHk^bO->zky^dvc|5aywiDK(5BqkXPK}GNKh$w zFJ9areHuAM9@x=p$JG$!dLrE)RE7?f5gmV|m$ybwCUb7lGqB)Zv!1cIx+{K=;Jnc; zXD<=Z*X;H}^HzxbL{)`!!maL1G^lBBJTcRfkOJ`i5>@u(+FxexPTxFJKcaY^?L01a z@1K8w|1Rj^v}AGUK>+~dp#cE+{{umN+Y>> zgU_wTSDe3qUSd|f!2xC(bc!pNv+;Cl-77Zsd3ABu7u+6?pOPa06ZE@nCG@h5@US%9 zdnuI1bd!Oc=Mic_9pS{tz!tKvFgh@r6cn5xX{6z;hjxm5pqj#b6c;nE^gbhyZY$-! zJ{bJPos$a+Tz^zw7v}UD6`O3)E+sOwMeF z)!9qqm4-1cN3q`Y9j3kO61BOoEQ@XRsi7q0T5{gedwLFd+chfl7-Y-IS&O=j)N7>S zo%P0E4qlsn)V^&j$U^rdb{u!`;17+7K6Ca2+zr%aJ3LL2*GAxJYCAel;dV`w>im3A zq}chEPY7*VCgSs!<+$vlm*+5CYWt~HEi~s@M@4@V0v1@}z?Xg&I4IE?h{0!>!*U zeS8iJh;+7Thl7H=|K+-m>9==kVLq~GeZT#mW~!$av-#*mNnRK+7@g-nXgmQU|LDSK z*3eiDyz2Ioz?*YYS!iXri9)S=7`zBKW3}}>#QpM-S2R;e_7(T1^!H0nR7{w1j+kl~ zsbQaLX6>~!9x71>(a@Sg5V24xk;U?~NTwde{eCq>2xh>A{ND3&tNnW4K^Ud36fc#9_TW(%PXEqzy zdeLp`V3zDB*dV++?vzfp42P;XjRxP!Ha_F`)W;2%pj%xR7_|}`?0ro1Rv}OJ@@j=( z(%t4^UtbqKw&AHRAE=+m&p=Sj=VHx&5?gTL9O-50aD-`-@F;JFXv>RIPh^oL?_nq5 zzwSma;bf#POG$Hkl6BEQM;J1v+11Q!?bE2~*0hoNR1ZYnocBa)O;HaU%bl)L0G~^e z)=$T;Vz`=uBE4eoN~4}-Ie+iUQXY`NZa!0rciLt~DNEqJ`p8zgu%BrAVVFm;{IVk; zC-{@7Ur3+elVpnJ!@Q5X0rHswM#~|=hRP*(fTF<)v`5Ij-y+R-|HtIpPD^PKk=>n?p9SG<3ntK2QvjdmI9;IM1*KW@0qUcbONXsBVO7 z^eUttj7Y%^Hu9ga6{RQMo|xK{gc;&24zAXio0NH(Cuwos1$)$P;k`9vXF2itUpeA0 z;=JQjv9IDo<)AhkqQ5^VvS@M^(V4Gc4`b&qIJ$9kNv1#wM$VF;*K^9CFNvl|yW}{V z6427_0k2E64Mz-77)5h_?|A?&Fhhy3d|DZI_8C9%7O}4z7o~ac3x4QraG^=g1;R5% zP_x+D6rAO^2_{s`$*#I|Kz;q&57Zd~P25d50DwY50055v@LR0rXyN?-CQx%UAiY(N zQ~1fd(!89U2=@U4zysk?uyi2^BmDsr5L(QE67-3*o=>{7j`}yfJp>AkY(}}-P)yL8 zxtenFgwa`RhISy!!6G&wTKo zaEmU~~CM zoX}%Gc|D=V>@1zp2AM9XncQ13dFzC>n!JU=>?ZGPf$_?kRh3j{+gz(meA=lp@Wz*%cq%A;g2 zs|C<5xzx_B&$ManN@o^basz<1i=1j^m|fs3c%)9(xn}3vWl!57U}hINs_(r^D{pFa zYC5%EuQsMpZa9o_e`kh1Mi?v2;MK>mxoEt1l$t!lX)H*>v$&i(MLhL=94Dov1KDJ>O)D=Zh;MR-DfzhC#x17} zNtP2MHzx9`(h=U?7$PWE6Ru|ms@Rm};7<*8R%fR)6kwiFv6C!Be2JE7JRe)q-v-=$ z=>sWOaWl0Clm=v@%5BJ~vdt&Uz`>I&2+1%~+mSK@%S1G}_e-)xIP|P%I!cITT8t*I z=%OwDLW4U(iRh5Dpe#MwQ2(b0+WElQP%=l zh#iv0TXHFp#~k;xZ|{h99Xw*N(XP7(imB7j+z)1+PXi{#P6To|8(i{6R5iYK-jW>^ zk>;Q-8l9Tt(c>*-`78NQDA21#HzQ zEo9G&{v%N`x&vyA4n-qsSzXC+S;8op)3Pr#CLQSZr3+VXbn76zHT)kDh|@1Sg1JV@Cy zo_MTqqYzj-??;)?lm)3GoBPa(kXZhAD*|uH|fty zI>%|dc8*{fqV3`hi0Q@t1P=0&`DF3|2iE~~Rq43bO-WFBdB9R#iQTZii|$u)a)Rb& zwW%CVleLaFFl^dApzT1Vu$wV?^t-RLH-%bKd1%qv)m0P^H$#Bm?zzU;F}<7K^liw- zaLeqBcS_$JXdwgzk=?2mZfW%&+3pxU)D(Ke!E`fikqzp;#pG2%&%@ouhlqWM6Rs=igsYu-fCMRFDT?pK`G7aXVI;V+smOwhzbV;C z;hAjQTd_jE@EV*hLY?W+$D`<)BfX0BK2pCX1X0ph>a2Bvjtxkef&)M8^wuHNc4yoe zbezs#9UxiUo+F~{XOnj%%uS)7uRR=Oz`*M|tE-YK&XMS=pgHahFPN1ENw<)7Z8EkD zfXu^%t>wL4Z5(o}v6~#X{RvQ7x;NCFyB0Z{e4%D?@c8|ivziRq0HF1?G=AfwZ;7~+ zhg2!gnsss8xPB*#c#2wtjkDCL59Ri2pU~YGbP$=st?8a==1^mClJvKW$)gwxMAVBRsON{>`$m1t{U!KGGr0FfHJC4Jt^~8H^74ob0OY{JfgKZ{eJt`q8?#vtfGkd1_h|D zBDfL;_40{IGTD2Jd~k9B%?bBCwsF^3FAi+`SBpVypSAEP1%}NVk4n!KbC$p&!-|rB zsuHRQ>LOQI<^xML2d1Rd%Nl&;;9IG~x|@FpPC7cT(8dnq0=8%cm9aiX$ps_RB&!__ z64NX?Q(Wu-nBJIz+89Ktuz2GoGA&jnpD6Q$2OGMFkdue%*lSwV;r5&YOb)hB3RQvL zv_$5PN2uwI4x?znY7u=!5SNM)?UQ3*1VbG{xrn}a z6JkIIqNI)Blonv#G{U;5+FUA89^KQjhB$t{9_`x`aOC=T4_L21uAvD(4$_S_qrhWE zZeic#{9GVo2hc5ahAW%w9FfLOco=oYPQJKQAG9+EgKQM(n$(y6t0?wBfI^35axGq2 zNx1UuxKrI=he@}g~3K1L zAlb6{p*}#JW%BJaeO`8g1FHzX?FbbuNGAbi5A(kfj<&;@&m*Pt3m4{c?P(XigdqhH>7gV&6IARjg1}&m5`^ z1d75%<;v&ZJrt8rsyM|OEi&cQ%PZj&`K|~ zIosR1MP3sTJd`qnxg`_p*{da1R0uDd7OJZp8)RP_Y9}T;1W6Q464t=u5WY}?^UU(; zmiwff_7YS8*0?+&+V2?}4iJw9!{U=h6RhHh>hP2}8KbM)urVEQ_`E}N^UcmBJP|p( zK}F;GwU8=+$Ff|)uZW*EsTLca5ZM=zsCr2jKa_pX_j=Wi)liFfPsN>--c?7mm zmp0GnZ!DhBv6l{b!g{Z^H}-DZ5AsU8_kWd}1$t8FOVXO#I=#p(l-ie6S)14Slkb(j zb&|GB@?%({+217PTtb~B?_XlJT^04Mj{4W^_8rT zT=Ivw`+>FpXyo6D5L}STcLkOyl^@j=M#eeDleyC=qj+E22Noa%&xBs~>3w8lY6DYh zfY`uY#;1sk5K%f%^8iez&d;JO1jfH(HMs+XKUD?^xxgsT>@Z$IR;DT*bCzpXARMNh zfTMcu6qLAUck7RX!LyMo54)%4I&}`gwMtb-g}z9Ui3)*dOLU`VR>Ti{Sv}xi-mPNa zt)@j{jdAq`@SHpKj34uGV{x6;cj?IJ=Au7-L%sDt_SntSd{`pxuJi4lA_A9zUpi!+ zitMN$bO7JmP*h!mLWUOlV$XkzDonPH&F+YVTPt9HQ@R{2LNpWGrfoXY*_ViP-%L*< zqpJ+pJg*aRLi?vxCpP-?olaI%_Awa<7L3^cYAmoE+DoK6FCe#i5GM*=o ziNAPiz>gh{geVm9IYi+Wp}{*K(p@O(Equ#604%|1oQ6#ObGobT^#_;bN8m?--yzRe zl>goD?}GV{v1wM>U$xc0jO#xT%=QLGRt9D!v=+9ecK?vfIx*dFfC4C@z4xu<<|GH0 z>7r!|<-x(iel?8kmmQ7kInVY`@OuSAdc-{k-ounL-wxe?>4ixI5_4>4wn8e!w*3b% zADkuR54KaDFiFvL#td7^SnpS^+7G&0+N{EnI87`Vv_kte^k)P!iZiaL&MFa8PPv%U zKgM(^X0$ev2_){5SULqHFRhkyUT!t00ICM{^iX3KluGcC*41q=v@D?U5oagRMOcy+S!{pI$M}H znIsC>Z8E@w+`gjYAlen+y3#f7zWmyLeDx` zTpT5@L8D&NbbHxyMYOf!+ZDKis9v&`2ZsCIsC)5FZ5)+C%b z4O2Em824P-E6T!hg(um{lWsT$vb`r~#t!0L(Hi(8d#X{w&Wsxb+mVyQY|MGm|~BabqWIl zkP&VIEug1xrvXuttJ3!KW8Csqp75)(_C9fU+E?$Y>({(U-I)V3QhNfNQLFAiW$EBz z`-d{Fg7C5-jYL4=*rFiDf2Q9s1ZhJ~&fkE2igFcoH#9XJOhdJLQpg@9@{o8aq*v>w zou<<_r`8*z3V#F_0+xXzApsOJ!!aLV4(J;Mt7AJHj7*{p|0rE>ao;RlM=TG zw4<$?FkvMC@T`8+aAy#%4CH@`qw?DVul9MkFg2uG{qH0ei)(62(}}#3O`OAOTio`G z07wF4>_G7PB;gE*ZNs^I`vo|`DeSvv(TzEM6sKrK@6g&Jl{Uitsf+(@8}(PkebQTt z*e9mNz>W5ocWW%slvFEHP6lCZ6Qg`a3im5sfHupULiV_ffU*{ABG1SQA4L{TH6R^k zmGlMxDPq-r8((miM#YDS9F;M)sD4_8yV z&GZJVXWIsmtB7}l-Lm&1GYh5ApdBUSJ8?8%shFs1F0AXe3?#O=xJle@zQDS@eTHVE z@s*fjE!5eh9U`N4eaLK$-8?}vmWdr<3YP|s{A<%CJ~#;mwR^C-rbW@p2e$N*$EAUi zDg6pm;+?yxi4XLvZZ4K50jSdcf?P{CTK`Yba9_=Jj;3jMY*np!390!d6Sei%G;!f1 zTtbHOlDf)P70&&AwdCTn;gOUz(bNY*!}<8rchJ28@5RfCS0(w;zESk<_<6*)<<=cl zsi2#8`*;@t0tSrubW7-N*JY7`2!@9~J3P7L>+ZTb;0pSIH+t`_`v9^0;%l zREut{eZK3IQuUhV?(k~OmLwr{`}6wU(`DT;F}&j8#}@(a&cql&f5e*t0tAA-8(6Y$ zLfy&w%p|F=FHuAqi!61oF&$|5Ky98AE(gvQ8S7KtLe3lWO^&H&sM`0=993kVsz0pZ zcElu%JA&Z19omlxa2{HKrxOVA#XA^!u!R|9i};Cw0}=I}PQl&)thaBT4>Id4aHu7A zURroJCdmHF3t4)&mlI-3_IXPZoKrd2ygn6d%lFCY7W}i4*sSpqdkgd&;Smh&%}kM7 zD%J+$a|&s&0n77vRxocPU{_0Ieb8J~{18irC7y2#4Cl37qvNbJuQdU&_Mfsnq@0zh zQ{_B{F<6B))|h1hs9R%$9}EKc02|*b5;`0l;Qz)~$VpEKPvQdrWRd{@a0C2*<136U zoSYpk3|*Z6hhgzQtc(9)SJZfdD&tHwzK-tr2>I`~CRjtrD#bbIao?ImTqINp8T?ODBOGV!SBy(qiLttqD@Qe$)T0{BP6{P(T_ z{_W@k`Y|Wt>F6rTpb!_~kAQ*8e@4U;N;gTpkF8YU;rz`7KNdnUDkfH1RzE79Jq`&u zHsY^B!e}5Yo!_gcKz*UOyIfhLFVis*@(ofC0R?s989o{?iDfn9hO^o+jg|K`iF&1h zygp6?o}vZ##mp0sv(30jU3h)(z*`s>js8;NTFc7@Kqn*Dp!d@P&z1p{=#&)S4zb zAw`&;{x>zMHbpi{^I;~anX+K4HtZ&M`ipeV*`YzMtx=TuHK4sHU0GeWKJz_70;bP2 z*vSY58vF`PgkbVGS&o>V*i$o`POJJAT=ZAl!X1Mw!Q84HTepxHOi1|0*^MJC^AyOx z#5Yxz2Wmc7eMobmqm8|Owvw~lVJk+-!wi=P6 z4A{1!Jwa+Z;iJ~kiq=a9L9fz3AR12ea&OT)DOTZD^7Pr+^7Vss8`e+jt}mSC-`y-Y z%GF&bocx~-Z>cYLN*{-;6F36>6XmkABcTC9wWYH(15*B;>ENdq;J*rD2gwBF4ROnb zT;E=a*k;xr7Oq-KYyK6n%0YKvMHagDo=_uwm5SoWiiJDIn14OC5`|ZK^sARHv%NYx zfnW*j@IK`Goz}`E7#<$(9?nV|o=zJMMH^nN7+zF0gf_F|kk_Fi>nV0eGDr_pn2D6G9~{-f zykk_Yg1+PwiW3WRf$Q)BvBNX;DQa0z@2E*;FOfLB%Zj<$<0x^Qe4(BOk|x+lfKK)Z z+*vGJFZ*8pS|~37TkPe~@c5)3k+pRd7VHyN7v&tEh#M_%h(wC;c2*+VaRL!XkPLAd z;qhHRxdj_Ur#gLr%WL71vBx~M;9r??WikZ4L+e;-_;KiDywV*kzUr3e7;6WK221dB-K-jV0~*|UZh!0$l@0_D#qT;af%IjXsYKOEh25gmalg6Sx}bDSg{3YOkzr)UtW4)>L#l@Q&rHY$v#oe$}P z0T8}6-M*XEb=LGI%S0d~Kw@GB0?(Bc(=ALf%L2F|fVBxkx7#_G9s`EN0w%mXC&Ro! z7=5ALp6Ol|ofSVJxBb=KexnvkJm=DLFIbi2uvg)WMZu=K;&{>vVJ}0IC@?K|&@1-o zaq_vkPdGP2ZEr9Go~-q7=tCO#!UJ!2bVgEJlGgwGtP`ms!pDNh5`|lTk9Bby3an|U$iUdf629?pV6T{1 zw1JlQWk(f9?*^E;dsY#pMK%p|H=q<@hN3V zYuovhfuc%IQ8x#SrDu>4-TkRQQnsry%KqQN?sKkrt4)pXp2)w0j3hM-n-9W^SS`}Z z{@mAZ<0BzzzGz8@Wm$y4KWW97@0jq{vM8UIfVYy%nqt&qXUXE~#uHZC5=q>QWlcuj zZJunvtZW5av!7X|Xn*F3Zan6eJevy!mhoNQxU6QQ0y{oyI7jV1lI%gs_Cp zZBkYC3+E&2Y?Cpx&XDbSMDzN-%9axxRDo~jaFry3Awc7ONo#%?WxDxHs%W=DIyqxs zJDu3Zi6ye>>bvpKtP3hM=*jvtY(a-*-JinQx1VxbvTk)&>$WcL<)p04e-I=V?1Ifl3#5am zX+ z-F%1OV^{_vK9ELFDPed2+NtxRF#B+Qoz7+o=p7i%wvUwDKGzRL;MhV;BhoO;+Dq$AuHBdcCk}pn zJ3#T0P-=DB=ioUWOb;JGVKT#d2(PuO4nN5E;vimo^tZfW`M^y`5bQ*NX1yg{Dz4L< z*S#L!$k1o{)(2fxXHDg?#`>`J-Q2^MY1sk#U*Yd$_V*ZeYB z9M8sk+ztj$dd2))b;$RNXl*;XfVao7WNw;MglO`zlM3>|#RRy%h9e8;~2 zlNtHGK^?6W_nv^VUAf^ThAjsVgPR0_V?2%tX%>GK#)plA?_7^P=om;jjedaygn@c& z=;+Pfv0&`IbO?OJC3S6kieP-s8}NiluEeBm|2Jcb5j!TLvm|of!OmeQr9$&;&y}H8 z3DdgNO=i1G?<;MKiCwn{UYvy)6gi)9AYaUBD~A&d%%kM!Q zBiHhQUoP{0yUbW!;6$t19q2?+$S5M(U2}o6cuZ(Qm8e(*(6WQE0@|-KRiId`p{09U z>$-Y0n+n@Pv_*u>t{Hy1YK&4Ug3g{AyOPUgk(&tXJQ1ts$n_kI@{LxX*U{Ee!*>)3 zsKC-&*VHq#(Ye-W)VN(Nue-alFyP5`rrK(ezFR?mwnlCT)(sE{TLmBcj&c$sqoR`H z#jwe3rLwANnW}bfHDbq#ure=hV80k7R!>xH*|$K0ysXE@#*jX2c?X>BY(?D5Z80Ly z)58%FiG%e{Dy>2uaIA(UGHs6b_nCf*U+0yPjH8K0QweVNcDSulw{c0$gy{_5^# zcB-?r)xHH7k|?y#L~OG?-g>d=(s0JSBiVgac~js5t2!Dd_Oe7;r|MV?Eftpug;0>Z zE9K^EkEQ0rmZnDfv$jI7@sVwh_{{CKbBOTP2(^N!lJjgour4b~Ha|oI&u+YKn+qMP z!-lm^1GuX!4-jYo$2DYEvDTiG@w6jin!?a>2>gez*V(iFn+kdg3f5j9vk1Ikp%yZX z!6i!=s8TWIG}UkVda)T`2t-o~m)$SYbSnbNWz$6LdDdXV=h8f-+MG9^16^-*r3$HIc)oC z-VS`yUd)?;Y_o~vEhg~><+(*hnu#@aR#8sG59GZY6U`swP_?NsHX0HqLB*oMyJe~N zOg`D@FWk(doV9c6Rb@Y(fBdQp&S`sKC2J&HWZ8FlLGYvAHH%k?WX1n|(+nn*yzJjm z=QF@UGmrJWy*xdf`@>^Xc7Q;csG;UH5p_Y&>bZR*tXwC3HpAHv^fQatp!(08Q^^dZJp^^Lxd`BMGGc)TuUy151PFt z7&{D$dY&|=3Z~N0edvSI2h=`AvKNE{B2!3W`pGs|D}zBFPELERk4<^66*jMG9TTbx zTxJlLK{_YSH`mTxX}p(v+-PpWEKVSh5o7IvI1~_vNEz-=a|^Al)bMm>JFaO)qd#9Q z#^}2f=Pzie^RC%qR}@Qa{|hmIkm~ad!W?z!yk)s9v^{+79Q!spKR2FFz%k00X za@jVvSapM|kp7Q>?ATm32dGI5LrCk^1W~XRT!nvclOR=u8+S2ezwd?P-=HDc4NBmP zvEoA{I7c)i#{Pu?Bvp?DML?m9)rkC6+U>Lc0_?)_5v&96PXdPPRdVKH z@y9?>K$L5mPrTn+7vnrAD>`9^lZz^F@w?;D+jgBe0y&=Un%d*NwnmXy_V!WIV5pwC zRjX07w`Dn(_s~T_7)AqECz@syBT4F4tz1~xuxu-Ts*#}{$NjRb%no|F-f4Ov^~C5o z_41!~pOB&JsCxpL4Qcq%>50P3jqgj~(jXunkKHYYSk?I<*qkvfjqI!{izl}Z0leeD zYa~q$(+6CIlU2IS-I8?Dp#KJEo0T2MMjBJ+%?y=}%K<9up}1xgo0Ws?Adui-A02!#dBM3-XMd@Acf0NvIT@2C1 z>Fo->5SKD}od@?GJw@)Bci;ZB*&d&rVW2f0c&YCt zny&F&e5?5-Zq{$L#w$FM?0??ijsf$M1}8<=u4ra%U=lW0${^WZ#ihtT^H_Zpy+@XI zwCrjB#<|lFCwa;w{vP_6Hf)Aiu7<)aF@2W+lS)}w(j$nFWJwOh{KavVbpDp|!q=_cDf(VywJuH(6 zbaX=Do{jk1C$!G=HL}1t^)}@8v>4WNMf{%Q)CB$f3|ZRQaqUuENUJmOi-?2l#n|P~ z(n16@s(30oGKvTU-cOU|T&!c4*xpLhsf0Hw7$S{ndKp1zj*Ddp6dc)WK)3s3*EccO zFKT@KuGK!qvtr*I%V!6GLl!L(U_yg~vscb(l4*{d>?XG%P|2niL*dcEgMjEX81pZy zpwNMlc?L6=IWmBwrzCFd=HT;`zXYCtRC57crW+dt#LZp-rFsCEtlp9e%j)cQHZh@{ zoh>jS5QOfJU?#hE`=g?4T|k$}{*!Xy;=6v;hBMb!xA<^9B0tFbcMg^FxWBcqou7{R zZ5b0%P3RY`WIY{@n_MLh0<7vs<|K(0@AWCQdRGB379ZgyI8e)B3bfJ0JHO9@2EF3|leIvBgjY`esQWB`Gv~rAhSM^Tmm(PG3ZM9U zy(b{4MLe*2+m`eLykQBdRzo0rlxNSw3NMT57MjiFClzhtwr_nXx*;o-GL^-#bJD@3 z)q)qaQeqXfmUu@Y?viJC#QZLGg=#R+u=*LWt24scYjT^FgeB_<2!ppOg^(0RXDs*P zsALH%(kLteLw`)?0>|iDSEs@hyB%* z)jINk!8`Tt8-uldeRns7x+Sj%^g!HD=I9n{`4{#cVv@nE&$3y>J_IGpPP^2lYe7oF zy1DQYE4r6}(_>yy4c5d$BB*x~CXB97F1488dlqe$RpiV}W7qjx>CoWkn-o032oF+(TsOM#AjXjjtKUzRc%I{!x7tm{5 zTW{Ea5Bd`i;KS*v6+4hk1E|%(j|?4m1-0)|`SYTtNuubz90{ z$vJg!*Dkg~xtuAC+Wat2eO{UAZnL|^7%W^{%6yTTkf#@@nZ#Q47Lr9V9!$mj8Qcgx zxoeDJL>Fygg57YbVe((g`no7CKQ9pj+ZuOxri`ey?}(xvAAXS+GyXcMJHm+m)LYD! z+2#=?XKSb?#6*(W1Tmg5LN4sOuYKXe1C!@WrKzgNs zf+3HxYyc0ZV%K#bAxe0hlq`K99l1V)14MT);6XZU%D=QX^HWNlTy@*d9i_Y$xqTMG zz#pFTnkdj=6GZrQzDu*SUME7ov5}-+FGveh`1%;j z6(JfhfOPInC|sS%09aRfp0X2;sG+9a>S5;>eqRH0K4;!R)=+BJ@bNZ3KLDy)Zx z`hD4$p&`-U=E5Tp{}Xl54JuQak>b(ODwy}C3Gk`8g&ol98(Y_EUT!^LAL?K8`xxlD z&yV=`uUXcbh>VNRkM3u?{qx=!_$Y-1Kyc@EVY6b+B1x<&#!ar(35+cdM%2)p8|VX{ z`;v>~PsCcC`x`>D9P+1!Wfv`|g`@IeTYN^Lc9h8=t;IJ14n<|TD6()@B38g<(CoR2 zF;q(%YdM=mM#6h5{`>`y^8mm>Yv2cpJaeFo6xh7)z~Pm_qza7nxG*rsYM$3u2U_nN zGlb$wa;ym)fi@G3ut-+t8f&BlI)ZDV7{*)zoxNSKxjTkB!mc5|hetb+mq=ZJ)$%q; z#Og#jIc_}s8qm}Mw7h-c8RV}yYH&fJllQW!4k-;uN5wx)u z0wYraiH&g}!db9aVu!^`9?YkDrC%}fJ2WKD;(zJk??H4jIGIGMd@*R{W?kC{>asdb zwVdAg|B>$sJ!YLc$2utBSm!qq^!Aa#Y2X*o&doCnR@&qPtQ(ZgTeb3k5vGHnW2Z1d zV_h9+C2;uy#H0bn-SL_gX&WHV$o&NZD6g93I4+Y~w z1YW_=5G^~GyotU`6ruKAUhhtB7zOr6m3&$4v3D=m%}BPZUF?iE?I|3~vu{&TjGWV3 zT9M4=W<6XkXN+SB1}6&Q9Dg}1#Ls`8Nc-eC37|r5uGwrwT5Zy~z+$sg>umaly|lRi zY#UHfGW7ZHoW#=y#fXe^J114l%&w6*ZedC2)XEk5Zo`Iy{uaMW$Q{M8jnQJ22?kpg zWa#OIY{XJS6DR}m6u1jn^dtm!saRe&Bcj_T&f9j$ymU$RF^N!+N~@5{v5fBf?K#?F z$*p<+MKCg>hxR%;S8}^>S|nOy9=clE`rez%yGuM7=9)^8Syu#ZEiv<0g3Kb>v&(FX z9&}IP&#!cZhdie=KM)swKQ!>fcrHAtAV2^|Kl-Pi(7Dxt=$ANxRlc!M^&4#K8+^%Q2s%KSv$+QgL>|JBUwWcvZKx<<7cf7PPFiCKKT6mkyeF_k}9#m3X|lJBwD7v zyn~K((gnT(A_@p&jg3>QCaW-dU2)gVeY&qna~AJvB3(HiNlY;Jn&zXvsw+bYQwbZ2 zc$NsC>bJ;TKA*WklnfLBqk+ghdU)-e`WDn>^;_GnpU*BLQu;Wv-PQ2qQd^27iL<>f z+A_t9=aHpRxJ4EQ zJ4CG#UhCgFdmAi*x```pMHb`w>pr<@2Lic8k6wb=SWA#bV!&q|OdM5h4;J4H?%W)P zJnJs#=~q3_TLI_m*nNj?iIXcsJC1>l`!HFTDesXi`^w8nXnyHuap^yE;Ne2M1WjFP zV{sqDNO113=rdKzPB8Gn@6#r()HmCc&Rs$sKL?OFjivtKL(=8kkAxJhg+^U_Tiky= zIrb~#NU~3Nmo|ZUvpRij{{B*>(lhvIMKY#@CN+``>di3muwHXK;mjsGRa*F%bz+GH5;lK789^ zVZf@P)xh18g#QH6!|)X?{kY)YZ-{d}jLG?karPQehKtu3Uv-JtK;=Em3#C$~v&E+} zwT??f2v%~GHcP3vKsoVY#mJh$Y(0eK%+CuxGg*?-dP?RD@&Aa#E4{qumeZbo2X#9O zy9jFj5u{%v2afoj_9JWt^-GRdG-_5JAO~)D$W1|BC^e<5t%XId7e&~DCbQaw*OW@+ z_3aWB6D;OncxP7WJCYI-B);uxKGYDshL7|+x+fs+;D?84RFQfNxbiKB>_yF%+_o0! zVH#d+$~a8(kT<8wr0ZI%vZdlK@fZu(!R;kA-+@Ebot^IP_rr*^)r?J0#7;ad7#%i?rgMUf@;`NI!P^pl~&Xf7{ren*-?6`<2$ zdSYz}I}G)G9=lAs1`wL3-tQaTM1PLjy;_zrC%H_%U1eFYw|1?9E-&S@*4Ge&?kg z&Aqdwko6(M{3Y!l|3#er#RA`u#jfTt0rVLSe?~am)l~%TF(myAOT#>Ubr02v_o3!| zE16mT0bSHlyy-Rg)kMFOv0`rjIpqxBDGPihQP%JO%}8I@#rL5SeoK)3(H`%ml6+HM ztt~(BnHUiM_(>7}$(k|qU04IX>`7_;M7~>HUggyZ_`l|n4EnOU9p;_(ioCwnuGW$F zb(;TW-zAa#MGU=GW$Wbx`b`cLz3+8FNH#)*`*qW>`X7uPdds}*x&49u-x&!K+Xe-o z5&(eTQUCzv|93{h&C$T#{=ZzkZ(a-;*EdS$IRTha?Rg{g! z1ra~+Lp&5Shk5ubQ~H6I|Lc-hm6uhNR@MlSRlSyQ^^_FNOtl4+%}lj)^{kbMFv#%u zJ9fx))pith4b@CMg?~0Fm$-CjKsDh4v$$|*Sa{cuTIE2!pDJ3)`@0$VyM=k?t9N7k z^+r*g9RxL0|XI%`Qst{w-eFAo7Y-k{wdQEE+pCh?gcJ@Cszm`pTCT}F`z}o0vr4`1#i-mIm5fq&ADoEgNjS=da-+rw5JSgQ!V7q30TbKXX}`7 z5>X7{=r`KMtE=a5<9YUu%IOXn-+uC8r7^BOlU8JsXEcf*20nLiZbsF%`CncYtri^J3vk*t}M95?fv*hEdQM+y>!^h}+mlRLT zGDDg#9}nM?Ut4gr=iD;q*JT0=x>edq^oB5}j{_-J;L7G2UAa{;nUi4JMpc@Sa2~(O z87p?#w0N-;WVX)LbtD2~Z87^FVs=y(d*ske?EZ*pBr@P&#W`e` z1}iV^G`Vk(hqGvVdVe-G?QeHQVDS#euAm*4>wl}T`Wmb*BJqwxp^MwX#^7=IHkqds zlJ4PI>QZ1%14b}fmRZlXJ3ev#2FwJC({yauoyn49QPv3<@-tC+i{C=$(m^A?g6Q#u zW`oe{_M@9QCU8(ahQM1)BD`p*xE(CW7~3%=$&gsn8MU$;_>1=6|6P|eEI(9$?2Wku zQb%H2EN#{y3;Q`W6@T)!mTXh?=;VIr`ELU|Yv z6SZ(Fm>1z9sSs_!oC!xLwQJRVxgIt{AxIOrhzgp3q_Bcs36&h_)Qlk`ADm}!NDlQR z!3i2+TCJ!R%OAS)qHk0ZYj4hQ^5Ua|zFH(g9LV_a% zHRkjpK+mc(Q;kzng#cGXYdqe7rEpiVb`7(}*A?L3%&-dSj$KT^v70l^pokPL$!?y8F@#4>A$H4G z^ZA;F^1;B~UOmpvuafsr8({eNeuy8BeaE5fy;XjMG&s7+*!G?sVz<{o35CpFP@^<& zmTNXxGU1;m1@aQp#$VVomL0~k&Fyr9iWQIP`4jHJ`#Ud>QJ`!2S8L3tYV4y<_bJCEgey%zfPI4WRFc4sJAWnZW`u6h1W& zen>?p#(m<7n7`@&`~`c4ATUKaiFC#cf`PHdmk_iw{{=WV*1H+Q9=V`IlA2u>dc(~n|=G}2HiEZ#&U-X9zga7&IKxK(ZBzJ0}Cu znYa8&T=nAxV3LqPEY|`3IWd0JFL7knqd@Nk$Z+}u@{;kGDO$0p2CxgLb5>jM?PH|P z0j&YFPI(tf$3+c&i+P$s*EJ4mh%E1i{+tNFwt;GLe<(o9cZaV)>@S3?gPp=dF6&1; zIi4pSB4k!^Mo5)EqcdXGXRa6W#Y&#HDIO zBs?YdTF0<}WK=@J3b3iGTX4#nsA4BeWJr#!_^cX>A$D%(^y)?QjXdHz`BB{dGO4|H zE_p-BfRK|{uwpihJJ~n> z%~eE|R50BQqiE9uSDypfP4N;t^O_>Tx3g!2Z+S8D#Rhm~>p;^nXyx9O7v~#Bk_@yp7dVz_~QXRotJjgivR}NTcAoQ!mm;ksB}@BvtMG@$fJG=v{N5 zb7E{}c7BI#{+PM_+L8XfS;LM~M`?-!10B|Z015u`*%mc1I#b>{yV7r6bc4398w0j# z+VBxs|1~-4RcFQM)V0xyzkb%*+&cKGzI9vZ(Rlq9S8>j%rbditcU(2)Q4R7Lz@c)=psFfjKs7`)Wn%9<@z)pyKUwd+IX9ybLbjNv#XlV zrW!8$ufJkF!;;3|XX%!(4##jtYt6a(=)}TNN^%{1#*?9A!A9-l;T5!;- z1Y2_fzStepqg36(xoDyDlkj(*yqu>*r4kxH(I}{<>J#x||3FWZ{_=p(XbBsQu5M*U zViP|7%ZeD+L0o@25uJ;Nz>+42MnZfB#5cgTH`Bk?1+@9Gy&1!SezYo-mFjyYd?F|v zC@V@6a7uEuG5=)S%I&dRFV|eKazWq0vCkHar_l@b!D=QN)S3S)X;HwVnT%pGV{-IrUci)QPnj)CwfoL-P04}hZ)Zqp6# zkESx{4i*c^lrG9w_fD}8lAJR8=x(_{{JcOC*(x{C@2fsmCiM3=q{D-LlMpvPQ{w}P zFW+__@xFU>`|lIyy-2KWAD>IvK-Do;5qcq5IgR^h8h z0TU*bX80E=rUb*(0iWW@$kB0ieH+i9m;(y>eP`>D5c|eWzU~n05Oov|7iUZ&q0Qqz=O2|5JZt5xAZ;-1Zhj=!rbb-Kb}c7U22 zrj_R{*B#dUA*Zf6R7=*u=;@~h{2}Nmt(r6gZ~uUdFk_CZ9A33XIR(%QFp^Z1;K($U?$wEX0{Ren2_J@HeI zwxAwPccpyr+dN}Z@p}eFOL4{A=wN3Vu=!YB`-CMLV%5vw>hgMVvI*ez+G77}o(78- zQD9<-7FCOHo?%p0TXdR^&j?#Z!6&nk@jSQ?=87f3(gI%t)nYu(g{+A{xaotpSrgk9 zz;zf)%7y7drt=YEFohu-*j#02J8#7!P6wk08~W|$;vY{(Hc|Y{;{1M&!l6vd*iqX$B~CHwYWu>Ln4WWNokD!=jI^Do5865zMx6KTf`9;+Rp zmuu!a+;)OtF&vX69KGeVKN;-c)m%~}Lg})`6JI-^grz_%etoXNJJe}P>YV|mav!qb zy!#uG^d5>#1~e_PSgxkQFXp`M*MI*sxL2x?0FKzm0s-Q><`e@c0bqNW;y(1vs}rTi?XG!&&~S+UJhq2eS`?6J zG1Uw5y)Ur}U;vZw-Rhs@Riq;%XOxGuBF2E8zQLvsc{MK9Gs*mr+ za&@7r0E~M9KpLV6gREz@v56d_Yy7k0{A(X&Opb^gF(|q zwBQd-nN)wTR|SBOom6Ks2!Q-?E63k-+P-L+@p| zIzs@;NVwqB5eZgOskj_@dF23HU|CvPq*Pgz!g5LT?~toF;(!j{w^J-u4g*YdJezDF zzJgUQ;VyDfMMEGhE>c~HnkF#1kjRF#H31hHbT7|e*fv0ef+~Q{aIGEH7c56_TY)N0 zO+I7 zxUN;1We8V!@KEnR$HbD=I%Y_ku4pzOGW2Q+*G7R3l9*BHh}fk}xXOCG-y!$CXz#>f zX#Z{P?w*=~L5+xE+5YHD7gRyvpMk#8Dp65;7qJC|tM8N&i)NdjI zGFBose}xE!8;*p(z~h!Ln=tNecykY(XfZZ1kB1`HE+Z83648xh4L=1L>>q5{seE0; zZSAQ0%lRrX(mj%b)67den}AZ7rIfK~oK{4~mlpX~tH|ZrVSvw}nEEVwQGi2;clj0f z&9I8|J-RQ?dazPkW{d$XpqnN8l})m@9J1$@T9&|WpY!MUAOY*&9Bd6BSwRZdJwza) zuPEkIKOhz`>wBBkbs0Er_Nr&CC>osh>IYgFrl!2aAG-S&bZjkfcQ|d(+mbKrE`bWd_0QR=bRd&&#>}Jc2pW zj!*)+&@jLsk3W8|uq z%V+0oBq+<2>I<3T$eqr@^QPoQt&ahpQbflW`4f7?*f)ML7kdKwsv2<-0R8%#i!7At z7cwCz4=WhbOkMs%69z5;@mIx75Ho2zHbU$vX}`~*Gmo3dLjaVV5EOR!B6r~#WE}*9{vRzIz84C2i~^Sz?wOu>PU zq_IpZF#dj>(4$z590ODZjRLDmmIil%5=-8EHOV%YXCxGDu>FkwdMV}WVx8%r(XwK* z?I3hqAR_KgmG=-&a3htX^VEd3=>=smWTS<7iQD$UM-|Zbiso}*KNi*|377YzzZa7= z!trc1O*L-iZXHfy+Z)*fiF!?Ts4$Hv+t#gM-=X-&{wK0%0D!~^V_`Ym39~Ofc;3q7 zhOy)0 z^=;mbn<=QL_@{+CF&9ghCFZta_N0&jpHnorXZ2qzwRyJVVt!`1pW~&LScI(1^*PYp z#SN$g2dQ6X7ejhhSs8NwTdaXL4rKYbhItW&gr)!(fXv`~@0Qbhe>SbAAR{0vAu7To zEPJzgNAg*dkhk5u{&?uzx87K|Z1*ZPAUa1oBGPsnVJz*m5pf zo}Zc)2&37oZ(Oa~e3vrzkNEm2GZFT+3w(VJ4eak5_dn-q_9Dye^asps-hxV_S%b>+ znE)FrE53~pHM$LofP*OvHj0gth?^mbofEpP4XvToUvY_EP2**qu`-k)5toVCMyZY~ z9o}Oi+69ssV0-AnwPmO~Q!Qbje}sI?UcFX|o0s;tb<)&k(_apTtzcz&p^pY_Sa2mNH3R z-k2prVcgO)aM~HEMPYF6-4=^o)KIwirNRrP)KTUD2lX-pR+HcgqV9BPP_bma5FS>* zI+N)I#R!fCa5Tronf)VS^ZL`!P{iqfv_X_pZ8sIoB~Y58U@wnOP#LhWxB{aAqpL}> zlBuQ;Uyh`J$H-6ZZAP}O8y^fZFy_<}$Zxd_h%7-=rhZeYQ~C4&`2I@ukqKo^(6@Zi z2f6O5oq(Ln_@P;5@TQu+abe`2Nr5vbTP*xOj&M@n=I1oH&$#lbw&?VvDDf za=_fy9;RXm@=#5`i;&O!^8W=qK*PUNSq1*-r?Wk139PZp?i6bd^9}I@yrd2GO^D6; z41R`qcA_`Fw_sFei0W3`bQ-Kj z;e^qrIe!CKmUV;xGId!i>zw`sNUrLM519!TI0fFGP{EWso~ze2(@D72@xEt0dUwWC zoc;dhiZ^&5w`xPf<(U=lAlO)L4m;d%5W6#_5|)6SW*&a|`&EVFI6-RkcCi5KVMxD* z$n@i`$rUfz@^6EuNSi|uCU!Vh9$*OrMp`8=`DTbx4TwYJAo5}ZqbXNsGI(Xyry(~b z+H2QD!5SA3*?3;_yJwqK6J4}YL@dnpH={QxD+XdI<53~ue8)ugPZfdhw^(Y)3twbI z;jB;ne2XzCC+~Kb6Xe1iA1gwhM!cHL|5*utFGMa4}fq`JXO{mOw(vR#N=46277*-pR zARj;9FO2DFudo`(!G+0@1c}9!&GCzTRql7tHL@}0D-gJn9&j!KQPj_F4|~bT2A$r+ zu%BR`$i#FG8*FzCz7}Sbs7+H>J7<$sPR}M5njPUd(>Ss#>1Tb z$lN_Q=Ly;)z(WMp1>@|T3} zLZc?s38fJ<%3_84o?ti(t|{1t*(o`AxpL2$mH|{*H}Ox+-X}Xm=h|}oV75DXzmjQ`2OcYd9DDn zHr@V>nPEGV+}OMcc4H{~3B4YC04O!5UPVZbYKuNxFd=tT5%lfh$WtpzaU#R?TyA|J za1XKO%7B^>FVZ`#LYAUKtsehV>2XT!<~hjDeLOR0FHla(dis*5#Pb4&{fzPw*7;S6Kc9Sd$%gj3^i9b(Y+wKSV6v>6tqxBhPovo#MN852a^C$i2xt zPE($Jq#Hbg)quioYi;1UtR+$3G#Ev|Tu$GKK!9P+oLkZ~nvBqF3YtK-xb-hsf|=sL zt_zec10JJ(!rS1`VyB-0oZw#^<`^BVV5jaqGY?@LbM!8`fHFjL5MP=DXXb5t?WlAm zSb^`9_%yv3ys`0@l|wv_+PV?wCD1&M6_?f>b};=0tpv#$^aI2j*S?u|A5`7q>+4^W z1&y4=AccoGR9l^O+47gLgH(IJF^0Lmq5AlUikzZ|^pK*YrpBlPzwTd*^eokypZ@3z z7rOtID0pVN@mk%ex)dFJ)_1A)_&LRmL&#!1997)QXM_5)t>#_j^Mq^f&h=w8cN=w1-29wlo4_Q-TL zF2m4rzMn#uZ2C)r7D3W^7&g-=+MeA3_Hn{~e6>x}exYwKf$o|O;w}S|e?Te-B2|8t z;pl{pf8WrF1N@4$ADkV=V`CR7SR#ck9kdZdjT$uRKE`4CXEp@?q@2{d{M4Rp8_7m% zmQc-Gucg6o{wNct-({e1hGDOvvsgWP?KlU~pbK)*6Fz)c4RiBzU;9N?Zs%OsnlsIZ zhxm@pmaXtm3AhjdQ~d4;Qora5?s!Q)`eu46Vn*hppp*Qzlc)cu(a)kF72i9Jd`8w3 zwji`b*Pw$l2AtnwPDQk5amDx#3iDfvj|Rx``KvSP#{~9QM{bjsQ%F{CfCSp8_t*%w zcA=*&N*@OX$p>;ykmU>%udqlxUp`&{jbCR@dl;H30mU==GgJ4GY52aOx42G2q5yxr ziFcVbY@BHWw(!y^XY=~+e74I}(kmXc;>yQ0WX*mfauQjLgLm?bxF+Wbay%CrTES!` zg13Wa`=emWv|Fs&A*0nLV!x{P1~lK|FEvlmD;A^0PcepWcpr@Xt{*v1iS5m{&2NU$ z&xgnc*1pXn{X<5P*+HmP;B6JVemn(+pc0$}g-GK5X7jvGH2E|t66GoSv6R+J8XadE z?bKa)bwx5vSgCcET^HT2V8Tc@K1I@Hzv8hsM)BMy_`^L4;ZB6Pitk)mB{AhP`m37O z_rh7{=O$&Hb41VE8zZd3D(&BLZJOPGlSw@tfiwe(l%1qPlIaS8gPX$%P4!VzCe4(# zGK-@~?OeG!>jtEeG=twD{ycM{=SuBPZ(@N&w1X~^;$T5{re!5&wh(`NJ$WA`*Nx*D z{5!Ow0njlW6x7~5#6jn%jc9emm;I$;+SWaYSwPTr;Omfy)uq&D<2Rt&)i>(I&cZG+ zq#im@xOa^)6LFJ~2M=I9q-$(>2wz|}YV_psY+e(wlah<}={C%%T;Huef~$DmBg-!` z!rqrLZI=nei@UrkZLl$+PYdkO&|RmA!<9B|wF21@G` zm4r$_sr6P$J5;Cf#;!gfhsMqykZ9=EoS8e593}-i7^F!Jvv0J7WY2&aZFRjBC6u)m z*t=yKF2k4NjIY3H*cku*qF1GZXiPH-%@mHQos(Q}oyFVSAYN_5G#d{Zfh8kCbR`N* z=XG(bnqp4N!>q)#Es-pO5r@B^25%0j%bf-w<_+hSdOj2AQGe@_>rLmLYi5zTMMsUw zxhbaq-Yoh=?LwNvRf8hvBdR)hmBr7@@;q_lCE5DX6ci$~+?zn#qMlSjOrXNnM&UUD z2Ua6~?k8nG-qzXNDyc}JF{(a1=uaKAz&C2dtdchIMGEdN_kOByP;YZ=s=Y|!icqd7 z)oELaY$2+IOSKe>VXBuisq-!bg-YL&-5ejd2y4*J{_A%P$;63dN~+|FgT17Z(=idU z9z??AJh5(t{PG?@tdCH~D2LOl8{x@fOR#yaSaqOB)^#4thjQh*Op{= z2=!qBjnN7_gn>?Oz5lM@qB$Z5Erp+H$q*^m=9@q7H8yB#*$BaiW)VM3=b%7fHVj$h zB%WU2GIS>%VsaDED#+@kQaJn)a0}M;L^)VwMOKYSR03gSJfh$TlnJF47KfBkS#U4; z11c^?8oP!uy`^jr?F`C>7`a1a_67Sr@JC1ik@Ls8$ly*)CDu6w)R@ao9qBzH;Zk`# zhZ3V<0d=i<#)ZTqAvx^1c9Dp@+0RYYw9YpNN-cbob%kf^dK8=9eK56OOAWzc+_)15?~TE{c1$1(*&CRd;8f(#jo^U&ft&sIY?76;F;& zdy&?A_xy>(<@YCt(P*Ph;#$gqTWUn2Q@aNajoG{P{zaPzqU+O2jqps~Fw^4%!)bJ> zyp(o`yO%t<4KkLAAvsCO^Fm5jlxKGhW^naQbbx>u6{j7N?~_XOANuTV3B?YzUK9!a zN6U9jaak_Ygay!I*j)lbDGL;Xc0ChlU?$BzYP8r7ld-|!X_i)1bU-y@xGGZQD72YZ z9=U(`SgAe9eNY3Vy3eWcS^>8FS3nr8y$PpkBc*tMSdgk*Z1unxPzP(-QXl$C;VH%> zOi#_1!%DvgMcb;mjJf5P^pSt*j;#xZv)cySds|X^s}HUcnkZmDKn&K-0?q`v2m+yeij$eMT8=;|K0Glw-5ZtQCI6(jJG>yXFm zDx>sjh|JMh9k>LkM%*kSZhGp}kF2#1B586?D;g?fDhmX-oCM|c9Zx$;N{NInbL+Wi zCTFYPL#aoU9UZ&)LR`Nq+Rl({>&Nx=n$DY8SyUdNJJpJIUnFLy@*1WFGAoS}QGfwd z?nkL09>&dPDIi=UbaxX|bP|id1{c+mXMSgPJTjk}LIf@u-Qu7p)q1)VgE2uWP zXuDP3)TMZJh2IgE5dF!zq=Z?5_i^Ag!qMW zd<_MG+1;O9ZUf*r=zAWq&)BlZr%%ke=2G-tB@WbuI2XG6#V}=7^?v5IMe|iosFvJfD&*X<%thPr1er zFstEVHsQb$OFVM^!|Ta4v)YPzH53R;E)=OJ=a9Z!Fb^hQ#L&+d}<_Qu&`Db*-?c7TXhd9OPYzFl11B_+kk6A?yH zFbIT{USJd@X z+ss1^*fC1yl)1Q_EwgXplZl)~o2u(f>9;tQYlH8HNoe|g;$M}JspUasOo5Lf)9+(n zGU?l03a9czsjqEtTYaiXKM-wpK;lWi9YnB-A91m8G4f}l~ai8O79eylbcsFuZQk{vK(eA;6>8#Va&V@1yb zq8DQHySus>_$TMKyvJOFoKkUY)vV@)W?L~gZWf)&$?E~H$*bSpfy5r_==KdwbGi|q zXp>hs@YY!o{-%0Izo=P zk856{N_}>Qdaq>bS%wDuUWEZ&IpxoOwyp8>GSt{Q*5}^qfn2BSfF3bi%Woxj zQ<#oUUVV5jMwpqQr-eL52%^w{K^TsL0XxRI_DZ5NaOwaql(70T)r+&Eq@wP<#J-)SRfn=H;9_(YzAhs>EHj z!iy(jjmizyz5-qb+x*}cTnDYM){4aqBaSMO^{=R9S=(7zWWyLpRg!$pj(V(l1Z;ht{)m>726_&AsgMnz}m1k?U$| zT7;VDs^TqSeHvLP`Cw$k&L|XN`&9dQo%)b}8>}PR8{?VtDPE9de;V&%`J*ew3~Dn| zn)I}MA-&?PqQQJ$mfjhNpaswv4w#OrrZ52$Pg0E^xE{33RLqFSnZ0@kx2jSv0U{YgUi2Iy-|H z2bp?RQremFd*RR-Iza&b_}*{uhb$%=3!Ec&asa1n7@Kq>L_UOq2<7TGAO zRdrC(?cQ>mCKVdX-3RR=7TYmR;!NuoEGJj%Hb0Ul7x(|rC3F0IEBwnVq7W5S!wW+~ zx@%`9P!Wt=Iw)qUUROwpZ(HZrixMdgChBu3^#A@h>EUQ)bci$f5~_F0=WbD#@kL@hA@ibM zhS_VJ$`h6@;mCoc7FC%&DBabXU4tuJoBl1tY1BL|Y~V8rewVuA5^%sMDG~W@{`kqA zLs5a4VEU)=z>#x1th`O%`s!bkVyilJ_8%KF#TObSIu%eF9_87#@S;${K@p?aijQL| z_dgvhfNW82uCbKQn+3)(iZmzir;Ie4@L{S%HVNiweVwTEs0PAu`GLS27!OyW{Elj{ z`^J6BB~x`=*xgHq#i%JXEEByKS?_9|s#fI)kA zAGXq?XI$K=(y$(v-e5&Gk&?11r*vorYxX#J!~yEJ8%!u!b$!)A9s2hcwOm`IdV6@U z`kN1svi+G&$8HX#ZNkDl9^O>Ou0}32(_(9o=8AQ_qmviNQNKrCr@R8~Ir#3w4_I(i zE4kkRevgm9PaZ_)FR0MQ#)JIddwQDoudy&_L+Tr5X_)=WFCi<+8~9WQ&}NI^VT^?R zdw&zyQ@LB&cNo_tHT+TTABf;Isd5t}RDQo7s5iCEEuz)pJnd{bW-L8sZP2ftUPSiv z{h%Tk7;0rZz!57&*Oh%q>r5P=)xZn2^F8VBweH{Zf#t6GcLe|jyYY(-hz=6KYs8KA zh64%ku2WDnIB2pCvTHAFbB%gg{alyUayA}LXA9RcLf?Qu2>oOipQd2pyg6`9Z8Mco z(41@UrwY>hMXs`_JJA9TOAtoWKE!rO!*Xduo6r2oyECYy$5klQyjGB?d zdSf_P4})74{l(WzIVG5G1d0S6%Y(Y^*MOe@KMTW(4!F1A@AXM>(2Dem2diWJ*-3 z25P90Vih>Js;yW9*W5W&D52kKl|^`4Ix2@bU=vhvpk{sX$ zpD)bLFEL+9pgRwbU~p(JEosQp<%%k8z@YTcO?XZWZis^A}&I@Se;Lk zKz?805t6i$um#YWCOz&+?n2Mxzm0*~``m+EZqT9}V^FD=%}1exhKtbq!b5C$xAN&g zVVmb7y{p4mS=02*I=(Pg?q8TS(i&OaY84|Qw!YMt!?R-z_Fvs@he1zAEwESe>cnf0 zlA6R0bRUr)i&C~;#3w3*P8(17Sd8i9Nk>-U+|WMaKZHH$Gfh$m4@<0W8P2lk5tat* zBd|+V3*J7ydt=N4y*hY7ZjcM6%K4dF9&K@o!pKY0H+ly<8x$tF+6ot}DN;+{M;`FZ%t zPFsU{MnU{nR(+e-O?;PIejuv#m%6!y6Jsa%XPl=`Hv|MAQjhdpa(wYiw;mT3*Mh74 ztFkWWfZfuIL|UjZhQ(!!rZqx%*tk`crCeGxv1Z>kh!e&QF=tmv)v|={&w&u;?G6@H zBH^7z_E&!qGW_ww!DtMX?W86(_0)0yrcOK=+m+1n1kB<@YX$sm+TRU&V!llj!rc2% z*Z-y$XE_-irJEk-K)iuukn7+E?{1zE9izy)c4=YJLJ#-Jrf5<+3{U>B3rjYpP21g|MAf=O+8%HcgHM<^_-XbhN zxFfx9YaGnnl?Z(dZRojQ@|s)G#HsT7(0f@{Cd zBm)Nz85+%2trtW2-)?j3oyui9b#gLuA|91^l|)W@8g3L8HhGUx^I*1`lYoG2l$3n} zUwlv>V<8HXB0j7^g$!x5mwnmSd;4q>#LP#By!+QBe>s+S9ySko;l$`aXfQT*(>te;^;{d#_A>e(3vo3coOO zjFp>q6qV(9_X;T9BVil^hH@QiZs=0QodqHktb8H{)?}r!{F;Ko@Vb7 zQ0;oS{Mqc53ULiU`BCseJ?#Bf8CzcOPYTJ1iz8g2z`gxjHbk={#N(+k&b&dr*1MTD zMcDWx!09L+rz##tY0^2Diq*DTdFD7+&kqH&z)E@NQ$+TU?5RNBjj(OaNb0JBdV~(A z{&H0x!8?XVhT}{c067_eg>8ZDWik{N3DYwd|H1217J6-@!RUf6Q|awqCSR?>OsRmj z5MG3AHp0iDj#6qqR&aZ1Ys%~<-bT6pCoLOuv3%`gZP>=hYwS)uMS3rxw23sl_^EOv z4A+=`+*)k=1(B)V+AI=t4LBb8SP++JwN<(6CYe{FYKr8TG3(Kg2!S~YCDlE&kbe}!^?MGN=vW;%y}UdT1yKw)Z>#jr zi(rYw)hsp$uX?e15~GA26PC@wli(54WE}Ki2+S)4LRyR6#DGv}j~lber1!?vd$Lz) zCnu#Q5bj`n#n!D$*g=0yW+JC&Iti*LeMg!4f{()ef*>UMwCqDzhDq;~5vCcP@7&r8 znCm$dz%oa5Hp(113JYH=vIbdYY+Pz0?x&P3e1UKwn4?#CG+OoF?~8Ka7@9>k(&M13 zJiQ;Qkt2k)r>jJLj{_6MpO|4zsNS{vv_ZVsj;<6-I%j~vb_GsS5}M7LZrp(z9R9n%g7mm zxhE9mQG;SK575-j0G;gIrz;feAzz_1hy2i>foU+@{{B_$?&d{4+j-uZfgWoXhv~h* z;}xuJ$JA1!5U0 zl2mSZU+YKP_iC&5j?dnl_>;dWc+Ae7o*g^FvU?LXJ)juLY%sHjb3Tn8b_`49SEJx_ zo9T=px8`W^V94-#LxNfF#`M#WQ0`Vn^`dy87C9j|vN`DfP|mxHjJ~TL{G%?9*|JfR zK)qqzb39xX9D8HCpz?#<$BxUqunhu&KPJQKj^9qrAFH1;X7S#r70aX)3CB4Y+I^CH zRFR2RqIS}l9AA~2RmH+n6kk%W-l&dxj}}73Di}6Hn_TlG1QHyJTQU`}{2=Rbs^lh0 zHDK8-#Jowx)lQq4^$(K;cZ%sKwfJQxrs&N9j)t&SHzj2&DYIDJQYe!31z z!6K52ca*PY0Q~Dl21C|zf%f7unv8>s4-oA}fPm>~fQR8}$M=4vX4~nRKNjFVC1p@h zgy1y?Wo9tf=Gr093q%{!EdF?mHXlsKhgRkl4gfS$HrO%cSwWRn|tFSxdt$*Vz9ZP_a*%uJ>>DVG%M*pM&GalLD> zHVkf!LG`j&eR$EvTh#`NW~xaT%^r&&rg4+DX+#mlx=N8o97f{IGg~EHz0f%}(YAy$ zp}prdiaJHXnzi#u|J(Qt_ClYuS^`UR;JV2W$(rmv9`vNDR{!qY^ zgLh_)*IGXLim3tR4i#Xrf-=GbK%8b=-UeN#69bdKt8|?CCnUkZO|ky}01QC$zdUx_ zqA8de=S(j_eq{$sBG0!1BgickIzI5B!?njZU#Q|uFA|o;vvgFal@av$(6R@Z1GLsi z_h3ubxr1xMG3?)Dd14l|Lq^0l8{$p8h;oiCfP9$zyAa*dPOT88VqxKfYWJwRm(mLt z(oUZHog(g6oWRI1d54iYmSX7{wnSF_at6D`Hw*y6x#ik9iIeCG z(8zoyFgS?jcMU(RDHHeeMYvs|4_-{CkxqIWEB&roZL&h$wyWfyGi^;cvoateDjf(kN1t&F+47BX$=|f_zmJru z0#t`E>FOOy$<=ERWBjEkV@rc9J%)d@Y)U9^_g<`kTBSVfLL?ZyXlqTHE9g;w4#z_c zZ01cg!$qG3g+_#;H*izml2pTH9RKAL{4ZNNXL5_-`eRGGe|rFd=HIZT|Mt+PP5hM9 zpa5#f45rN*rj3ouIWdePV`yd6OAsL#J31;@{~=4e?5JEKQxdAkgP=DI1~J87Ee8i> zo0C)fbGF@dPR4a!96gYihKaj}Zx1{{H*nlY^6*vy23$8D{lr25j}QJIH~m^w*> z71L3;6+gBj%d$yjcM(xqrfZkvUYYk^6jcy-7{sl?c0JW^Zho2)mOy-mUTq>xVkTnJ z#-Va$rL_wt&$iB9oHs_szN8C2*7uB$#@EUX=q}~K=Dn_O{U#GY+pmhS>W1k+aG7)G8(uTKZqFB4 ztzX?B_QB$ttbrIBE2zkzw_ECuF5 z=2i+8W)IWFrKCH(0cdwWw33~|p9U)AymB2)K7@VT5J$h2|0 z7wQGCbbHk4orkL)yP-rE%uj$bAz~D1Ff1}*y%ikfXuK#^hRAqaxKSQ#A9T2h-(`o5 z#^_|ngHOP6=iR7Yjq<>K;9Qau&w`I&RYtmr>N(|*4|zG4wr4EQx0jdv`V!8o?;eF! z!HkxuA@S*+R>;io(GW$`SFOj>XJ*(QlEW6Vplklq@Zt|{)nqEBglu{&;a7TE8zusN z-phCo8h~q7dL&bYeh{p%dFr$b{Rof%Rb=oE``{dWF5Y+0dk0;bFGcsMs^7ksS6sX= zA0&Faxo`RoCL~o>@mLt4nMt9nCnasPw^}A#M@@?i${KoEFw1VTjG37Su*yJ43wIf$s*sRHX zj2h^y#KVpp$xeB-hI_Dr!?zGXSJrs2RcHw9)1BhI)1*z!I}djI6^uwLZb<^)J($%- z)gCdOsy%ud)&v}vEG&1XvwJN*jYOs(()Q|kI3vzk{DdDO2zEo}DRwy=VK7iiXYMphQKX7r-&M%FG)7Oo~@f1U~& z8JRda$pUNvX8$GZOcZ40*98!Lw2@_mlq4)(fv>=bqlZU{z*H)P4nk9x&S&SHR#Lib_RuGm-EzawhO8p(rWqAM){FO7B}xXPYR}nI+WnQh2oH zk$F|S6div$n81Si5*OuY88X{MWVc`yIXUKcSQP7M-BBZ6%HD0GaJYa$95 z958V>GPa~xZXq!%i5Qg`E>%!&iR9O#Qug4r!{(z9-SSIRGd%!l`ld#Ipfe)Yc<1CO zSP5HCQSV+{L$?h>V{M)LTpuil0o(x+q@zdbYz-~1PbFK@j;yiGVI$*=#2zdIi)Wkr zqDlJl(aSA_4oNF6M}EWzgpb%4IPPbZ*$xR6hed0gMx8{-q?FZn&1-6q2Rhywbz zqdLC-Q+^j`3+w;QfVv~V-rmGf#KOth(ZbN>uOzAJXf14K`^R~m|BLn^in?|S45+-O zn@k-*K{fmDM!O|JrNAS?8c6iQ5)#7ss-TZ^lD5iru_&B!`iSpDzax2Fkdxd+_r!^^ zT|iml>y0&^U$VWf8hXE8?$G*Bs+qjEwzuK=J(1#`B}lhIF%Y_AF^)BfNZ5(heN)Me z9b^;TC7&FJr1|j`yVTV*Pixprx2BDsDVNri@Yt|Xo!&n|$5pYrVq7WN;EyoX_9#!IlXn%p&NT?{ z@?EcOK2o1$dEM-6pedR9r=QO8gWk<9^T!#UbtnLO3{7iav%PM+dqOBMZDaO)qzkRr znZNQUFPnQ$>Che8$@Dp0xUi=B8(CjSuQ5=g3c|M?Y;>w+SkyL5*PA2onS>&Nh#-q zEQmag;4ta?T*sWZC$Ts%lI@t(qV`fUe$)A-cY{p0$j$B-A>gC;cZU3=$jK zlX`XSJlTTMFXJe3A!)#ULU{j8=aqrtU_tZeeFgISjx`GF2=_1Z`I}6Z68%#)OW1?<-I>XupkK3L7EGTZ&3cy zJX>@=Gxm=*_R1E-u#O5)ivC zV#0WDK@e`nnCG>q$og~HEZ*f8rdb)dhGwnDk2jopy?UIckKaf2>GeQ3V(KvQH#=L^ z+KpP;nkl{4b(m^eIIXk$a^*#_HiGNo;N+qP{^Y}?5b+fO{n#I|kQ z$;7s8XJXsF?>Y6qU(UUCZq@zN_1m?(YS->wwY%3|-L+JvuQGLqoJ&b20%p31D}7d?RTr{+u1?W%`=ZOpFqTrkq@YSf`LDp{uyUBU4X_X zR3^a^0RQMoR;m!?r+EV+hkhG^bDT=TS4li(xp{NFCG2W_kwIWA#bf zF$e7$6|n$?MySRoaG9A4Iz{2N-w|}k_LT7%-7FFN&83btHDJ#JQ!}IpL+0E}+NTR+ zLJGx}4qGr9HRu-M4{pk1Owu$QSGaE0&L3}mqgzpEesg3Q&c1C%4q0bLjIO{wKC`HU z{YH23iXk1?{l3s1pqG~v_o97Q^HC(Umf##js=r}S-ja3T7QRoR3NuACBcRA4P58Kz zR4T==_TL@l9RyrR0yVw7H&FRqnb^JhnA=){@Ca~hDQNT~ois*BX~|DFkyGIDhf%U! ze$FuGWH|;p*|{q6%zGN9U%?K1@#sa{?*U~;@o0&TPO~J&id^PF?5h_m0`g)!E_8*_qsFlisFP93i|Y!q3Bfq~ZD8VDDi04)^WM zO`hO*zWH4FF#DHJ<*Ky&GP*LREc9&QnHqW!RfFbAmlqUnOOsC&jF-ROJ3u^QV~adB zP#!OFb2y?z5f~b6GN@an7Z_>NY&(TQVk!6wRT0!xKAN%OGvL6&)fEUgQ#M1BC4`2R z3?0rNSsAX^2QbEaP*2%q#o(7=3K6SMde=7eM_MC?ILTKtK-uYbD42 z{{ZMeh;;ug?CN4^26VA9F>rFdwPAsM0Bn!t{4k z&FtdkZx&^#9lQLQG%Rb3R~XUXKRf`5bVsjw9KmtN(8vW1b8;lAv2B~{rfC7X*T?sB zE|7gKL-&sE9NC^_*iCm5eANj{3FSze5S?-W=QG2Kw3@D}k}j-{gQ8NyWLJ3L?^3a6a;dqDYuB!1dXzbxKwN_ zb&i4p`2vh;m8E^ZA^f;#3nc=J{aOIY>9|S-d7D+_V#UEyMO-T&nyXog!&3{0 zIW(}qp4D-hGYk2}-(Dr+UqKv1h+*X7x(o7#9g`B&Gsthhlxz;hVV7v670dXw)Wj8* zMz%;orN0~`89-F6ZUyRLVzdwk?*UDu_XXZ~n6`$@7MM#=)nDT+*=$ zPST-Mnm+qDhZZgOrC7%~N&Bc4g!7ik4%Q%y_cu`c)0hR-M zI1Ciw7#p$0+`=Zs4wh#P4%X#ib_@d1Q7$Ct%f8aL<9ZQI8op3C^5+_pHiGTrDjI7P zx#+4}OCK#Gq>M`x%LDrwX%I{U#AWdC>gapkjV`ti+O>kz5QXm5FnP`FBtSiwd1$tS z!DNNTD2{j3dIekIdqjd{GjHq-*gSa{mE;wjFB&T_RguKd#LPVp&E4#Bv+d)tD!?burx>0H=Ze*YU!ZiUU1hyQ%+Q2wjBisZlJ z=|8|ztO?_Vua5KGW6Jes3n<%Q2PSJpAS&W~S%SqOpEp~iIVwC#SuPvt+VI|xZtiBD zo<1d9Oc;ihx2G(tj$Rv4z$wCofKmnk41p>0DJ$Q0BdK+k?PRXWbB*D?FMQl>*mqxd zUAyl*c0aG)BMHD8@=vqkbGz4--UNJFTT1R~jPW8jg;a<5F94mD6G7IK0#kS5sE;u^p&9z`i--5?^bvl%tF{Dc*ZG8 zrkSzn36KQcNS^xI9HTY*9Zet>RRS`p?_k@~^oX|>-4mxF!J6%)p`lvb0us#c%a8E&wvy2L)le_TAu&1rx^_19*2}<4 zqK^>8H`J0FBnUxOoEP>A<90dIDnEM-cNR!${*Ioy_12{(GH!j@^ia&e&pOx`Dd3~% zq&E^&A3zLVx0Y*m;V#CeOdWG2B*(VKYHoKMos~@X2cJ1x=)?|Dxa1Io+;yB%8NNf` zmdXZ>HmKw$L3`wmAs|g^Z}kHetRh5o+0!Rr(G~hwafVinqYSe=D&y>r=zjJh1E@ES zDf3R)xm2Mlau$lS1G_gP1KlAW>(T?Kjl41Rv?yB|IK&9YWH+>R?EGtotMxYnR^o&x zFm06OICwfRv8|5|yakr$Dr|7u{5TVSvaWNRQhQ2`{IE}RrQxYha^Nk`>gv#8k#7$& zChUx7%x%MGu;M^L8~Qe9x>WFm_6~-xHwtfsy753fF+#QwI3Zn8X1+vQWr$;IppWCXGi^tDcD90Do3y$Ae01!9_gWvD%8s?PR12{#2l`?ifbQ;Rd!)HuOy9WmKtkp{t) znBght0O)CrYFi=$mAJqLJ3xXY(2M*Mo2}L#T83%)E8V%E6Y$M^^|(hZAYw)izk={c zxdBAFBJ=%8b@z9l)a@E0hEIaSNkPdbAb@cILz19~7?*V=7w z;0z1;rB(cmR?43^FBpTOu(I_q0lAg_BT$_7W4?{8)Z11pgMLlw=XVb`a6JB(3TK+8 zC>>(~dZvRFeg*-)@Y=|SZ^%G;&Zywwgz#CviXo8=blLi0)y}1K?ji0TaaH{YcZQK4 z3>78V`-EOpJ+D*N!l>P{3}G1tNXaBz)fW#^dcUWBZ~2pE_r#WVy%75JI*8VYMq1M- zX_fF*07T)?YplmJfMe!7$px9&jNAp$3#uaBB|V9-*OG18vz#Q?DQ_$Uq!r`^Rvw=Y za}LeJ(zK66yc7~;$EOF`xn@u?N>96ceM}9>7`W0oWM2`K&|C0UG6|kHcru0}=9s?6 z^t2K#qk)29+5-YdWl+8aRJ{PCFS%#QF`G=-3^WaV1UtQA6E zSfgd>+)wr|9a2%gv-n#w;SAf<51i*RR8XyRbXUKt3V+KLP!+5_n7m*ay(`J_3AO&hoglqTY-J8qS8YwoF9gDJz`Gf-d4uC96p)~vX7zi7 zz;n9F8?g=GxN99N)0elfkh(2Hp0l+&J13n6r&x&^EJ?Zd20G##`%fL)Ba& z7+HSXx6B@XlJeXl|FuLo!ciGhNET>AWqT!lGVH2-r2~y7*=dct8x|KQ?8{r&6+aCR zTutbc&D6ya%iP3)=S*H)UaEy{1-%j)BVRPRT)>WbNo-1#;x~rVma_*7( zpQ8({Y^a-D;Vq^PWsNj}9iRunqB!`cJg|y-kk?nRIsP^Q-EW`QYpH$jvAvQF&2NHf zeUhIkdIWeRSKFt-#NSN1&3DUJn1;AT`&9ym*B%D<-0@>G?Gy&ikqZ2Qxj(ThE6WfB z`)n_}L1>K@S$PcoKknveKEPXc;}DScfq2`2@3udMeNb+cty{#wbBpw;|EjvLhs^xd z#^d>7DhVGTV82<0Yo8-Tb>dt0PQucOca`yH% zt`7g%1^sUgG~Jd@o|dwbnyET9%trK{sJL-}%HMp*1przAKS030ZpP`=<*!p8vtKPC z|JrH+XA&UsKN4umsJ%cYHs_fv=j%4NqmAbogCCnC5xpJz`@i<1w^NtHKy`K&tNpuc zbn=PXa^yCdnLlPJhcSMWo9s>GMWvrlWOxHI7w5RY{Ir*9eVcSw?eIDjxmI@HR@fyckk^Oe$2>22)oS8#Ug*zlO! zu(7{x%;*A^u3cRc@!7zt>GWV^lRH0232ij+aXBQ8fVQo#%lg0eNGqzW{E@~`LP^`s zaG?7c>jcBrN~|Qe2Jjx;X4m}~P(P+t7-jmKAhec$&<5Lt_DJ^C1meDTF0SDRl7& zj6o{6G4mocTcE{U6Dl4I48;tMrr$eJraH~|^E*eWrV=~P+wa@nUiXvlNP;qA6Gi$Z z8?~KI=i?KV?MtSn?WQrOA_=QR8^HdX|g-T)rt(*-im zg*CBu+SS6L1rIxQUwh*GM=0ZfknOIs3(IG1Kly6?k-#%%odkhqp zYPo!YEj~>)^+j^OO43s;-y7drQ!c7^Hoe0M$3_oK8>s4L*f)CE*qe9KQRJ#>y8Wz% zSFN3OWlPOe()qeP%w@+%{h+mMS?F=qzpNuA0I{a3^^-!MVrff2rv>)paZ8?s543@C z_CzaOMC*_S`tWpxOqan`#<0{x$zpA_K6VRQ>U49qWPjVyu(aVIV_307kCJX18|a{; zIL%_!6E(``7lL#J6DQNCnrgem*>yk8Tm?-l!q6VW;uq^Qcco>6PeEIHYU~GXP==Ysg`Ip~sIftshS^0CfpE zYOPsWAITEubkki~;=K}%l$T@w<+gfL`ak9Y?uGiNB@)&u<^CBAO~5%757zw>4dFch zWoPL52UBAYK>n|r~R z66>D%Iw`xaz`|Z4nQ_DdP1>e8cPB-RQd}lup1QFGZ=$_!p5z`&s|#s9MIlKMJaN%u zlAICtlKtF9`8?t}#JSCFYkd!08<*9EBJc_e5vkV5eN?=01R^c+wo)@6xH?5jI2xdl zHzU>iT`-$5XD>b$k$^+W&Y`(%H7kYm2n&2upbms!SfHp2PC1*R%nPss`@AXD&WE9a zCf`qTn9~Q<@hTVjgdUgu!@^&v1WU&N+ZSYZ>hB_<2p*J=u|C4SMV>e{xc8W^g}ZhQ z5_Go6CIB~^`W*HncW!^;;j}v%tMUEAL(e{&X*K~HxdS2G-y6VJskuVqf};?2b}B`i zvZDB##-cSqfc~Yk0eR%;G`c*A(+~Rt%TiMoPCpG3!>R>&G~YNy_y+eaOh$7-lInck zCE(;|^!Nh{wS81SB*PYzqggwm_Ev*+yH&3-Mhq%#i0#UoH!iC>kWwBtEyyl)bSLGD&#=dZe z1RD}8?-3i{(BHl?mzzEH8HK?xH{W`5VnERyNujl>D!yq?*E*&FPc z*t{-d4b|^!#|6z(!XM8ZCF@9wsgEa6IoaMS9%QsWfT>yHh zIjD+CD&x+tUAb&`(S6!c{CVU$b^P(kV5(21O@0}ZNdJR~x$g{Wjs)k#AG(hM76_P`hg@)`AziP~9Q9M2H^#Ep8ClKa$2FxEvHat_c`eooqSZ|> zrog-P%?ED};gd6{{?v^Um187`QQv%Nj)dq}_UT2napx~>pg}<@L`!X+P}(M}xxBuC z_!A(kop_vkvz#IY^A^i>L9rV3nXTxIPr|T8=tVqVkp0z)-mtkuh4ima@w`oeONw}u zDP**zN20*rk`-}LSF%r}1sXJQtMTDIWo2LA=)FLYmuMOLh~+_=w`+21%eD<*)bGfTc$O-8u1{rlR{Qx}L;443sYr;&_R|@c)Y(bilI8s2uhtyl>l@99PG2x8 z$4Sg|0XE;>xa+x{;N-6#IMJq|@z-#hF1nU40?@*2@;ngEjg*DJ6fs3dqKyb0l|L@y z7o%>Bh4M>-0nE*R)~8tMXnEHt&|$@MJz2L|Xz-$%ur4Ij|60ir@o?j(TM&~3QKBDs z`FDj|w7FBJV&NVegQU}`)u6X!;GqkcMPyMM2eSw*GDAYEV2cJ;3-Z=iPnMq2O!z1> z9=}G-?MVz^k-qn3hUJVDM;I5jI^hvRg8xt+Zq3a% zM$Mogr{O6OY4Xe=KUh1m{nbGdd+2P^V4Z5xddx?DLtS&Hgv5H*u9nR17f#j;_v7V9 z!r*vlWU?{^=~jHU8v6l<9*1x;UW%XA5eLPYca4BEylvYvi7j^2*jQt80c*GM$H&G* zw#-#g)8c%YACI%8rM(TMnxOWuWNx@kfqmN5e1DQ*IV!zn21NJ1NChORJ7y(vbrqxm zZAQ(V%oe`E!znjY6qDFIRr~dRB$~Z5-O9hm9X`|e;qN_*;f+QK1qHgv&~sX*oe=R* z{F!^ED-5%v?_K#)@taJyUrXiY5LN*Ny`%N$$t6bU1#+=P=+Gf!vfCYy<&{tfzaQl} ztZ1b-&7eXiZq&f1k#LqwdrrcU%gcZ~bBLmel|@q9;*3@j`*8;5`8qs*7}Km6Mv^0w zc`eDjnBrA1=j(yP+Fp|PWlpF)->?~+- zW*j(Yo&&1?EF3YGQhuvQVj~S__OsF{fQ5_UJ~d&zVeSmI)h^`@5!%K5tDGWkBjFSt zQZ=24DaQo%eNR9r=}@fY)RsuXGL?O!EHRx*zxXaWD&P7U21mf3MFxm@n;>|Kn_T-h zHV-9ySS)ZYy9qFSigp$FLhLJ-b_l>O*WW#lM`!LaU$5#OpUSO^0574F0QR#}lr|A| z(P5Zm2o5z(QlDnNnCBb9t1qxhm{>UL=yWUp)tRzEA0{@lvFDcDRqO+cv>#Z1bOD?% z=@@slQ(J{rnVR8J&;Zv`6tb&i7C;1ZcX`#ivsvJ{+ncN5s?@Zi1c^7;<50G_I?$)G z<$+&(J2Xjt31^c}lJT#?eLa7q)vB0oQz54MM5aVzcQkj<&+TN*UD$*TL7gPz5yeWF z$GnrTQGTZV&idj^guigD9e2C;E(rC|?c^uMr8x*l-=A73Dpqcb39AA+AU3ZyxZV#h zAKAeoDf+S7SH2j*If2Ke4!tp4NLDpTR&&5zvDjQ@hJk+yk>|*dJf&;4M8`v`t=3TF zix1ijxbH*?_o}Hx>S)?Jk~LtRRoG{v61;t53hoA|`edFqmV*IzRnl3UixjZshH^A$ z$?q0ru97E1QZRBfUTJ)W?_lpqp*V^x9j11!ZvK4eC7XT`Mgg#5FVtUtYq8aHMHp>Q zF=_oKQkBw%@A85D42l;)yA!n`C>$v_L2ofn#5ovqoEnr(OUj70<+J95j!#02y?@N) z0(M*aXVi{Jy#}p&_k#!ov$#xxtVC~Fk>yOCq?j3S)MCC;(e}`{BTvhykmSdjoXK1RKco0T8$OO zusVkLZXVhW0^~C}Cjy0Bdk%MC7I)aDcuC48(-;rrtck&C#M{#&8dssV1n+Xyd?I(u z0iWMDs_86+ldSfb8T%3dK?B|OA*kU7Fa<8IK@bbhYTv`2@((PZ7R$5MWvD;-E?)%Y zqy#IEJ}94f5HIgux`)0}X@(al?qxa&(5HWC#x!ms67I%OkFdDEIYt=9(p<<(i+W>j zwOUBasXr+Ay2hAUrC0%vbdnrGGyeLP|ob!&)ct?WwG$eq*9I$^IF7j(|wOmZA(&{V@do z2Mqbc6^r-#VXM$C>@6VSUkH_iZK-~$=2E>DTl>evn+ecsq}oUA12}O`MV*0lL0H4MVPkIBdPXJk(K z_sko&3;aZ9;!`TsD66U4Tc|@4iq)kO>JJ1=m{4acPy>$^u_na}(;ERin#lpE5)_hR zg9SP~ir`|wjVa}>^sd80&um+rG%br~bY*KT^^TQHDfINQOxfK-k@Jz~=rF2@Xxx|( z-k`M<52^tx{f*>JQq`7(MXuD4uRdCv+jR4gx8-$%7)!0Zm{w%%AXaFqT}a(f-G$ws z0D3TDw)fa&>s)hlW`kkoNMYnf7H4kpKCEl(#&NIuMkX6?W96cI$A`2O=DH_gEbK|| zWp(nP>%l75y$SK4!1|szFcdL8YLGE~HV_ofn@Hbhxv zx#)5+`ton20k(_+e+wWZWwT;z3v?9i z(D-I?4v5$2Ys0uzZel62M;{Tl$HS7=4rB1AMwQKCj*d>E9H=EDCn}xQe5HJv%+scS zhiR|{Lza8JLvsXe*f=%*3IkY+%38zrtnBX$-zoi;jA=$x){J3n{0fnKj zi_-MmuIL9vt!@sQ@q85uE@X`m0S@06{W8(moJr^?96QeV?|!=Lu{l-lH~m=__YcAg zLHnzf@XvGvZV$&ckSg$#So&F+&`;z0%W(WpE#gb4c||$$T%m?oe-E=`?9~*gljD{J ze30*X@Lz~3YtUVogazLMxFER1fv`nSwA>#1$m-C5y{_JkNuiuyvlMx{l#7y`F(@8v z7p|8y-`_MN+ zwJ)*r@%mnoQxPL&@(nM%B ztJ8|$uK74_FCIMw>U;hqj?vX6MDf@=-9U1V)+*x_%hvyCC_1Z5*yr){g#EXcb&teL z(Br=CJJt#M?yncKiCb9az%m?i?;y=!{!Wju8Gh@o8kAZjhx=bhEr`x|(>xN%q>aQ0 zU_bS#4odw(xe%O3&^y>BF`&xAX5oW|W^j@0arUN-5Je&D|GJ`A-%L9|nbP;5&#A>B zGP#O+DaSO4Hd9IdL`7VQxe2&ImJDruX1=x~+6C_gYYOE?kcGeh;urN=gEl|`N*+Yt zBom*O;p|m{iGC>ehVmlby9NkULktjpxyRT;Go@Z5$MA2RS2emY+8*!X!Fke}CO}zb ztXNeh$xndy6SGaW9K1B=?d>We-CGO@r()ISPTXQSd#yn4AP6=vX*zv8%0TdK2|63a z5V3E`4$llYFgo&dHH|p^$)mq0#XllJ4zQl$%kv-*ULoEDr$Y+II8Edb*6gix*9%Y) zLgty=!7ipC6-5qIfZ$j$62L{QfUst)B_l6VgB!$L@zCSghvwIpqy9d_FaDht8-1g? z8;6zPaoMDF>gM#3l^#5BNvgm&85&EoBat}xELmSqa5Z`x*|IS2S4Sm3urFyYa~pj# zVdSsVF)|>CnLi@P@b~kA+RXvk=&2IchhcYj1<9HYA3v$jLL`}V+&_QPVIQ8<+J)O^(M0UMu+z}_AXAUo5yIQ&h0~TDU4k`5+SvtBJRs4NlX1zjSX)4sTt{R605$tMr^7;4`M2Y zA)xk8(|aikSC67U_1&m+tQsjUtUlFiIf=N}SRQPtTx-@K%bliL046og@ZVQI0kbSo z`|;~?`<*$7B826lT5IY>cNG2s%@T8+XN0khLo$(sR_E9k)P6Rwe)6FGWOh2N*wGnj<@dVJv{$d?uLyS@ zx-92xsIV;AEZM1JSc_S&8pj&8-198EX#qab>8T^<%zM)NwaW{(Tuol2ad~$_mu=Jj zoxM(ZzC+5kEiYg^@03OAri}nod@YLN0}KC4Z4766*A#7nCES`N)S9&93>p~~Lf(BF zEdR{ZiR%@4vXa6n-9d?`CGM8&!YOx#Fw?A@h81S2YTED_w8soI89hK)_hf~}5aPIc_jMuJQGW#PdeJxPmIA0hQ-H%tF53AjsEhK89u zq_qjF`YlBtPX_gknG%X83^fz`BEWkvdfjyik7v-?CDf_hJC;?scU;Qab@88z+S#=y zw-fw#&qjQ*XV?)Dz$x@B?MOPhOXHmNh&j(nd>-V80AL?G?;bz5q$$R;d`??fei~!( zqB9zh)uLK?Zw8>MEq%O0XcYFzSwGOUD*WJd363XtOBj9f{TKhrQT4G-gaHCFOY`6O zSJMAG|0)f1b}_QG{XZ0?(VB2xI%XK(zS5bVo|*jXHCyvt?ZChqyLEo8wSMfV80q4q zdSFtWQJXw)p@X80;@Nyl2pIzf1srA3ZdA6Ypv+GXKyAWj+N?G7C_26 z^O{g)l{)qKEIY%$v)|c$|2gyeMX`;=#z!ccGkT=47c5G)-)FQF3K-qnV225w&V}wx5j&1>O%3AyMD~1A2pC$(Z$GC<)lsu^aaCO-YRgvxm*sOWec}AZYD2t0a%bs^bXxcP`{@@OEfh(lyBL zsbJ9WGwQ5(>865&v{Pi$^rT}J+r=JcOIml?YjhSwGG*LxtS&jp#)6{CdOMf-VqA)> zoUc^YuN^()Jg2>!1a?ZIo;!i*I9%C>py3&FzmE4CjtJxrST=rM>oUnG0fI!5JoEF@ zdMG`2<$Yf}u|sb^IF=$|(nbvyAT~KVYvI{AsLN2P^JY~)D3>N}2s}d$vbk1ZAe_O6 z#o;ZQrlHaWs>9c!wH3R5B97RkMdJF8k&$i?Ot&p0n6ulGqc2*QnLNUV+boz2Yr)M= zNT(4Y&{xHiPO*fALVR$9GVyuYOJIa+`2|XdQ+NsoQ3At81&@w5QLbz&?GV@-; z#LSb5Vi^YhWRlNhO#n5cXdds)xgHwRn*LK7*H$v6(y0|s$RlqE%H2^g(Oe)sqE*ey zuU{qS>DDfE7_TbQM|C1{mLF%NDluJ}6}5=Z5%`shCAr2&C7Ow2N&SoAr6`2Wp``bA zq*c04+tkiu7e^xs)Pa-A6i*{IApEJi*<=vCDt{CQrhVlwbW&elgU7Toe`GYENlP>6 zSC*_>rw&))Sdb&;kZ`Qhc+!z<+C8$V-7%WV0ZN%67=<1ak{Ari_fa~WnT73ki4n@ubWmg9U|U`tq_2S~*Q|8RV`;i-HrI1lj6iF} z;m*}TLObv+mFf*+CC)6R*xt#`(~My3CsM>nq}tR@=H@utjYzR|IMyyN@T|;g%cS|V z#&y^PP;rT>m9=#lUg}|Oq(d1(GN|`D@d0zCWh`;4prypYgpA+kBgTCcFYM8iWk7v~42(jmnR9djz3>0e&KKH@IlQOulKed^aX#)rP8*1Sep*4>G& zj-v$?u%g+0uH-h-3+f?twy`lp2g%_@)~3KwmLzL1)-v~o0BSXo+|fO8LJ{p`Gn3N4%R{zaaP&|TP=UqMGm*zXO*?L#Rq!GJoY5Zvnf_ZZ z6m6@v6otDLt%qu^Y@=#(*(c?sv-w%x{s{t#CFW?NeYMnA&D(ZWvdAe@WjJ7xCMI8~ z`pY*qALer0Gx~3ZDfc^fQ?J>6mP)s@+>(qN8hk<$-6=yjZO(Rz{b#Z*8NPorOn;`& z?+)SN2ZOzkt=X<2IGzcqeT2*KSZMT1;T&jHYY?f!d4NS>C()7B3KRV`z}+6{8S4^Y zBW!-N#rV#bQWm{9G0*Dtu&%M6;`b=nYNfUs6tzNrWfD00grc$5Gv{+Ys>loNON!S+ zrv^$l@Mk+X%RJ6@h++f>=}m)`RJTe$joZ*gm~N1ej@s=miTPto zvX-|<{nW*G(wm|d9HXNQ3-4?Q0r5I!$Vwc-9CGQ|h~q1Q7laCnoR%2SbkKqFf zf@??og7X7p!=WOIL4x}92BD&W4u#FmhhZHa+GTCfOG7q*pm6FWv8zUldIL_l`qyFD z6USJq$#B#{OHh>l+C|tw|VoK$Pu6=5^#$Ji?`An7BX~0R|z`4}1F3x(t`k$l1zx zn{|Dfb!Zr_ye!2&RJ5PGEK{1TXl#%y=K?ndFcmDkU9}ESr{t$s=hmStT&n8YZmD!>ctTsk?PK+&5(8IH*Sl$L2p&l!hRetVpV)h(FNZ1!Y z*>Ap}p3PX_q8p=ANgBlFQX(d)RJc#GesQ1hW+P?fhl~FAzP#Exwi8CQoTmWR{AMi9 z-zo*In+t0K)WfA>G-mQLa%+&8`?JH!(9{CB85(bDRKXWGxgw6(m|1#(#+2gDL=3Be zxVfcrldxqF9eLnj;=tT_g*ir<@HUr}+Qvr{(ob6LS;q(ET_V&+nmH+9VueSx$Ll_N z17S{?;A}JFzO=!z+G^QwG@gS`7&PwEDc|clOV~RzY;>xtqw9ycl<;Dw>n~PEl_Og# zLb%gYhs2+XS#s6yTM65u(M$_J_=rzU79Uu)A}uJqao#Ds5unJu6W5_ih*T~qWH>Yu zUGS5cN|KFJik6Jai>M+w5{AWU-PN;58BsN*J(^lI=Ff)^rR_0A69mYgq}vtwm=Hns z#BUQ0kiuHh+G`xQP6(+qqyXd@dc3+VGC4H&Ep1mxe5yz9dSD3_;~#nF!eeY^KB?y@ z|Cle96|{d{RISk@pGkWCtl|2XY0m)~`pK-@omf-YG9oNljB-5~B0P!;`t^Y|Z2 zsG;7#W=Vr={@_|J^vn4OewcpvD+9WVm$%0Vx)hrijzLWs_ZR^^9F9HQkzx{H!$8^2 zC=^Z?q@oFKRwK(Y)&+TZ1*f!&Xw=XFrMj8<*I7p;RtL1}{ol<4*L@(u0UQX(H^%>m z^5=gy5C3ETJvIKH!>jTC-a{Ait`Tkkow&-!oON5R^XQE_y2Y4!~HA8*1sJ4NES}dBEj6uh87beR_4(`qM-)>xKu%Op*%Sp+#jMhaRHZMuG7e9?2nM z)xFZjtDG>bOoMnCSfOl~M}~%OS2d_ku(5vi0VziLHe9ghb033o?ME3ayGealB*X^V zfkg|GW?D{Uxe{3^$#=2YWzU)%jJ?=q)gPW$BOk8l5Z8V&hki()X2bPsfG$`CI0T({K)fd+&^UK3qBy^HKHqIH;dj`BQ1_i45-Xtk$gHgl&BoYxr1t zGo;;G6*f&}-%*N89_9D7nnR+R!_{;7ALn?-`*rZiJwHT>cYD!^(qt3f>o>B<6g+)< zuC`r;)fw%R@O2Yaa@Ju7LfWtEu5oatv(wW+lVX-d8d2l3;70o1h7m}D6nhg~giaDUtg4>|~D$-QD}Z)z-0nel-7-9Fj- z>mvO60WBq0-p>B7`O_vtWOdTvwt0Iyb$JAX`$ zZ@A{ElaP47pYdEZV>|W#IT2IT$Yu&M^J^xl5;$k_4cWIyPaZ0{2iaq79gdID%k>oi43t1($5E7O>ijB}r z8ivh^$N-i{lqS`m8OM84IfaRX>M}XW>7MO!VpP5um{-ha#eFgFX(!4-( z?hD=zLNy6O%{j(!5@mwdH0HG&yDUW}CmZIF3!)0-$3)qw8oed?^%^x5`#)aaF-~P& z9kc@#-l!dV;E55dPpP2^=a*-fhjI&Q_K?Xn0D5`NzkWNA?|6a@cd9L}9_j{_ru%po za1*f}#d=3)7AG2G63U_jkuO`rqM1@ZQ|B$1#DfhxHoI~Bs?^`Xt(9u zPj7-Iar$bQifKPFSN#+LsuI7;EJkg_L{dBr+xl`>QL zqz?;IDG`0++gC}XT}XM`cC^LHM-LxhLw9QdC_iHZpJ=i-Brp+32oMOXa#SPLjs;At zxZ3V5E3s{fYn&#D(a2WXSiUrEFk_8MwAkp&t=%3F6LxG6coM5fVD%tL_;jIjk1;c2 zB=Tj=_;y}}dR^N6cnkCgESO=994%LggBU@00s&Fl4RL$}w5-90a`W_`60z^*BFwXX z)1L(A{B}lx6#&?8eIwNo#TbHWqjvC2a_mvv6r#uxTAkIJZu~hRp|Y~2E*=w zX@Zr*h=w6LiDxnL$$Y|p;$41VNccy#K16F9G@0&pZaT8h%miisga5_=y~Qp}8aP;B z(Ho-FM_-bu%2|)yu(eMI#-J)MJ`k6m;S*I_RF;$;eC}y35!g#hAps32u224xa#qMk zDBL`T(YYlPp@-l)y3r@zh$yY3VmZb$H`cO5Xh4EZ?@-n9@zDwirtu1r z?UqB_j$!kjOj<=KgSl{nEd@jl)LONL?Fz!^2aNgQ|B}zJO9wMmjxsX7>E1jO|xBkM&fUdms zG0Y$Kn`Jsf*eZ*4c|-GeYID~lGdq)HXl#AZ1AqK&ICeAJOoviM#6{3iWfGvhZ2nL5 z7WO=irWgw^xw}(iK><=&ajF?PQw&ibTf)+u8nZVCQ6t37hYDN zDwC5KE~ytQUPTjwPFtDMobtD*@=^0zn)kJsDmLp~=WG}MWopo-Q*q`z*Og>3F^F() zvGZ|AHc@~sVbF@HlCCI zGDi@ym^@g$a-YY}0>w+MTZ|8*gt0YFL>Bs5XD z43R!R)OG(HjEu4j{xR}2_;`%mBrrf1ap!p#wC+&Z)S$4;>NegxbB)n}GQq;^2(w(@ z*455JJg2JMv~0ceW#Da7b`=evW|r4(lDS%*LZG%Urr|c-X4!D}nMJU+47y8e#_k~! z0uJIph~L$zx&Gy&bF(zp+8S&*Y-6l_9RyMW3uI3lzjy~`f013}yL}Em7GIdRLFU!L zT6BIS*21HO>0JlW|GI|YAcw7`2RhXbe7Qk8KVo7NCH}l9Hu=RZ68SLcknf3jHsy=L1I1ru%e?0s& z_!3;yX_cDLtr-9}IMX+`FPghjc2G!uGC~~aV~+ho)gx}#9vf4<@AiO71KP%0<-Z0O zf*X172zdQz7#_i#G#7!!B$?m3dloT-!P{u3?uZssdq!PF6{&2GP9YXnzqqWK)-zY{V_Uh12;V!|n;$dOS@R|1G*=i7-SIC}JiPV#Zd$CFIOQEiB0QfXA~SY zfdaBipSUIVYfTPc?*5!1Yun5^LQRsWL!;x^l9~jRcPevB?vrSESr(XD781PU&iGvy z+4r&=G^mDwFf_54)0tFf8lgK3zV(aT72^12)WWbJWhGTajAh86!LwasIsjdthU?Zk zHq_MioOrYEH}O!}pxs1677Js}uJy1GJe(Xrbh>!<-RgAJ`>SNNKb9Aci@w%kXJV-y z680PN-Ys)j(UXaXdQ$Z7ev)wiiy5iD4G z`Mg|$p3v#qAl}31^yeB@dekGh105*=I`%28IkfEBk>y3t*iiyi5n@y!&8@hm$CqM(lmdlS zhisq6%P0H7A{H19~|X$9Y@WrQo=POMW>8+|(_dUKK& z06PKue*xPPoJ*X4y{BPuY7;XOJn?=hpPtHpL|$3JadnG0T9y~cjBIg;F$ z`lWlL{ZQIk@8Kx(yl~D&QOTXoQNi{-?RfKIq+KU5G9ZLk8M&zP=>3|&1b@NI04h&& zd~6E@M3A9zvN4OZ!uz@zt=QaS!~0X#YW-cTG!5SII^5vbY2;CVqIUw@iSDt6ny;0P zRIt}Uac9Df^S}y|2;|h1&FPYtQi88k`@Y--jF9o-i^RS_TPIYf{Z|V$8vZbCq`d$y zE1GG^a&5AZnq^c+d%1~Xy)EG^Df0qjRMKZ@lzw|y74>{d(ixRMYAcIT$HcjpC9v0Y zx?1jJLt^cR(I~b$urOR?c+%%166ujB&dyDHkV#$nC^76k_mkJVPHX3Y+OihGs<*)S zT!C;FQpb*KZQ)PIABGK>T&D%uOYf6xZ2OJgYv&XZEA^b&33R$VFfeo+&cBx=c5N2QVrq>hMCI!n4}I) zH{n=U;wI(p!}@8@uJ|8y=A<6i@Y{qRcG)f9w<)Dl)^WW;oY@&8Dk?g#hiDtXgE(dX z*9A#z&@cIK3*X`0(fPf~V*3dkaOjDxR3t5ak%ohaf0KZta*6|7e7L zB4<}1AVr+Tkw%#angp5du0gXp`PsEBus!FieN?zRBfm=6Y&ZJ#7fpo=oXs7Kllh~p z`D#Q=S2_}5_E}?=VBEG{-O=BB zl`Y-ieCsSyka&CL5CSrjr$z+pwx!3Y-K6(BZXi(r2(falPykOQ9*f^rRZMQh@3JJn znokSV0qt%loEOGcZDqk%tutku*68hf>^ZfD)+9ADn4e#$THO%BJBu% zIrdR&WPbg_istF>S+QpJ`swWH^UG26KDK(0fcL;iT!{v8=gGUUHEZ&qr76SU1b!#n zu<+}`AJ}VW4zSgz9ViX~;&(B>4aZN9XG`s&zSF#dPBE-=-^(DR97?^^op{e!Daq^i zd%JXdWJfI2Cy*2@#h~Lb}bg4phAg*f=u+JFVQyt}+MHA@o#cXgT2$6kFvGQ_|4D7H22@^IOrCNeXS= zN2qp`mtQTFg(2M->o)bxe!{zxFyBIq7>R+Th4gd)lL?1a==`( z7{&N*V!dPAH#gCWY2ecRlGbmU679}PCeUkRu7A){P+HtwD}nFWKy@};R>*s;(C42a zE{-j@R|P17mKUw!TB>-HpUrwzmInt)o4meFAft3p{sgobLRN1;;l z$j(VtiHv`(XE-(VP;;F%>Z2{IL7NYUG)zwVD`^XAk=YMdfsM!>x|pN!#bb4SM(JFj zT$gOc1)OvylO#Sr^T{^fyIKIsnrvmtk5=U7Uee@)vS3xq3>3M&`;IJe{Z*w%)@*p< zGR;uz?}(O@J6*7W$GVCsfy6VE6E@l1eP>WZsPay2&l^EU#});m;|6gV|}6qHy*bZ^6_LP(IfR_>epn5rXa20jm7N zJ>->O8}oeEIDoC?L#k^bfM|92%R`7Ht%yjG1{BGcQYKXcN8*F0`XDT?iv>}G7 zE+ag7M}Q)?>q6%9wUioAMMn9RHEYZLtNn$q)|)S6zBTdun#D;!T^r(=kpjAg_xm<7 z0BJBjDNL0%1yN!+aADM4Ocg%%*{N#0=9}mlcVo#>x)9;3PU;Va?R32gqs!LD_HlDiv^5U8aAq|7Fo`O!pI*zyr==10bGDWRd4Lnj^( zW)wy|9D;Grb7&N9`XfWNPIUoIG zi)-*?$Jwo65L(H7_<~9SfAFagB6r}-=9=vnNC@i0vjcxVwk5RQ7!hfMI0=+wz1d5y z7g(~xni%d_J2D(-)sq#WY5sloVs^I&!`@wh+7sn_VQs4&XlvV{hvE>boz=^a#wV0* zp-C&IzN`2Cmxsl9*;S#F&yaIrr4Kqlk5E-b412IW5s4eT_|0Yu>kdw-lP(j}>v;HK zF@DlW(ufd~P^fimRiFAv=~QPa^M|Dj(FBcc?jcm&oA|&}S{ry6#-K;dD=2&&|~W z{x!Em09lbHdSFZOs%*!Cj@=26k-b8rb>?Afga0jY&G4@FP-4}*ol6xqba$~=|NVzg zq&*)^3F^|Mx{mx!$~|mo0|e{kh~kgE48(b7!61uR-3`ka9uLt#rVdyzLc%!8{05K4 zo8q;2tcFpW5IQRHu)6EGIn5oTy`yw#9|=e@G8NbS=YGz$|yX@OAT5_K$SjQ`*AHjU=F-P`40u>d_kMH?ek>gN+4K3e0)xXAhkHP05 ziC_9nj%~YsMmjaz8p~@d+{b4~eJTo(nDy%=9le^vp{67XVLSNcjd{X>VvxH8x7+rc zzc?1N3keT)YCPCsuL{BE+|Ihda#@AoGL`ll2dZLJlLogrm2%Z)2qVpq^3%$1_#K*h@U zcz)pkaKEBq8zm1)8ZnL1HnC%$6RzqveU8I>+#t5GdWMA%!F>__#`0v7R#go>H#a=~ zb9Fx%VoV4zd=DNb(Y|V(;%7Ry8Z~V8)Q-@5X4^aR_P2ZL>Z#LS|CMhN;#AN+f?a(; zWlLlq=^2iN*+s}(SZP|mMGqXIZez3^ERWwpH5ZPIWakOGDeYzmzTGT-Qdk*ieJzab=s(3> zuwmeITAzROcUk3boMbBFg*NXVR8jxv{gYhjrRpg+s^29i4m^u8FB64l-lJ$2If3>| zj43&F5#JMy!k7d7dLH!91lz69{_Jnf9?(8pM>UhIZjIE0qrUn+{>8Jh7D-TTS-CKe ztaGuQ8M=S8OZFw)eGH9dT8+0vZ=p`ip{1KB>?IOIQJah?JXvBy_)1SMa7L|bR5--~xkr?^{U$%f;wYq;)qgJ?VE$O+2l z=$1X`Yf1k)FbvxI=>`;PDrYJ*ub5Nu?Obzsy@@ezl_?0PTg%GbGAYRsVv0E4J9ayE z-w1p>9u>sWc}Wu#}6>%DbijjLziw0W;+2OJIRPq?i7G^0F_&E zl=${WuTW@_nyDP8r<(|*pyMUfJC_``hftyrBXvS* zpH)$ZPY;Xb@27R*gat1cX%}I|PAf0%HXmUg>9vZi9H23an1XGOtgf|LX4Q>qex{;A z#|GdosAIO$8719A7?n`G__l=MpzEqVMS)15`Nxt>y@0?d=^ zz=*EWm%F%HjtPHxIKaG|M3S%zs_b5@VuA!`F}0X@2`&>Hr%zH={AUKsptz2Eok8vr z;CK|_+G$05wvMPAk*IIP#un4ws%>+4+ToGGhTg;7W4oJs-Rzar9@y{xh$!85;C>H+6jE1Jf2%}*lU+Ubnl3)vnoL^QPVO_SJm*Uc+t@NlD& z@eKQIduE3>!@~nS6Mte5$Pk2gZ&ZMw4R|G1r?!O^MOCH8($NmyQ})Ar2d-%Xaw zTE8Ub>0P>97%>sjV=#Lsk5ggvK~dGxuY1oPb-(!A!v0MD2t-uD+vKRo@U2R6#vCmi z)@lnbV~UhWpUOh#IWUg`O|I0^oinzzplnZ*>DQl`5-_@c$43#3|hsTjFJ*||sm zG&u15gs#xMmSsY+{L#sQo_{8bWE`D(Tqck2&Fl|N?z%2q)Ad)?0vF$DkXvX3Q!;jt z?C6e*miJPdL*ol&!-Zs)_GCTY&w_9%u#?UkEQyLvTrtOV8@Ug#>G!vjtvWA=7B%&W z7!HLOUsoejdpO8Y7M>luuL*LBW~{`^Uc9P4;;F{E#pNVl58qhKeA2;VW-pOR>HgYm z)K{H8ts@BcoQ&bN@Y@oJVre*q=Q=wz;*3Qo(t{$u)i=JbFFfpX7?yI+hQM3IGE~c$ z4l8beKR*$3=vQMq*Av&`LWE%Zi?eGl@MKr-j?*BE_ij0+QtcAEa;Tm&HEn4sqY)OA zPq{aA_kGHOSbeM)3DIBU@RBmB>q&VPF*Fc48)MHpG*P|RcI(!6PZKZeOAl)#afC1Q ziSSsug-{mw7BWQtsURkaMSNrkDQHwwZuV77GiwX0dTUBl6~5lze6cCqXRf--ogI1M z8XJ49Q;JaK>y$S88J=mI#o~uaA&oIqL8}$zQatIg%*^}uv95-R!FJXfa8|qVQcpR7 zTh&EMuR%u0xtl$O%F&6&K@*P=%JjRRpywHfy_1|TrT&EpR77S@VmTIj+OqXECDkkD zJz;Yq1HQgdW%jS}zGZEDr1AoXCGLdY;}Ee5g^m<|YJy3ascsR`{ePxT znzDB%agLR|kPN9;%<(b4T`EWNJ)kgIZFlzulD7Q!%Y1)ihho=gsH}AyjBmQ{bWRf~ zv+G?;eEe}AZN#i(?Mbsouaes}DbB)TJA7Muj)`);NBH&H^ERX&mA93vM0+J~eV3D^ zntFq?>&{+IyMstx(-WuCF{qlkY$Ad4jK8Ty=Kd)B&S6|h6miCW<;n1=0#n3PxPlc# zG2o8Ei^)(8i+y#4MLEp%c&nNiA=o{vCkYzE{i;gyuy=pIu&6iH;t2))%F0eq049l^ zs3>#(wZ=h?8;HCUmoj9HV#suADkCJtm6K(vWZf!@hZ!D8O26L1^T?p8imSqz2{d2N@Rd>GIe2Wz*(`Bp#bVxw z^BopB#IU@o`CxKbuHtF4jh{vSeq-M6r6g)5tc|M|%K;@#tQ44zs8BWJUs4=3lM2z0#T}B6H>J0X z_jRaTx_`L=FVoCJq@eNeH_(|(RWgZj`;~;q>&Kr>s_P=zx0qK(H)mV0Q%Frscek2A z*=0gBC#y6cBG0#@Fsw0Zy(R_do9EJ%$96=932YnMP5&*I@<;B(SwYqLU6*=YKh8Wx ze~7^%tyLH&AU-4G?zarWR&LJ*zi%Lg2uS#4*dOUP$hz8ckER*P*IQEZ2_w-DhYV z_u}bHn(U5#3OA3uVD){9@T++ywOp_7%s0!TkmNLZ?^sn z;Yz2W7C*`Eaa?PE`gM(6!l8@H7ba~EDwlo8XqMIVFKX$?S+H~Z7%F&nifgZ+cC7SH zVmGC>N==$91C`Ie#Sa!y*@q5Wn?)>&<1?5Zyd3q?+-}G&NtC#7G@6IuW@*TXUUPoT z1mQ(KgVy6mc|POA=~Ru zve`EVbZ1u~E%xba?5ez4?-_otiM)ecYWPs+{UGBI&4Y4vo$z2X^2$-g&2VIN5 zHr8lEy*clnJVSdTO@@WtF%AxZWL*rXSlZT2>Ft2EAxsPj*>1F@lc5k!f9PAQW#~&9 zgV|9A#+_Ss0>Huo0MMZy05E=f1%Um_82|_v|MZaq0flkG!d2uH739|=!&xK9UIG9h zYyjZ?h6Erb5JrFZJ<{lip1#R3{q+PpsY6lT3;~^8Xf57cU+0kV2K=hz*XaKZuo^vP*IGQz1xJ*_Y^XqPpfZ;&oy3cN&=KDr zDS`DrCLmx@7-y^;(VMtF%PRJ(7sxlFRWE3fwO-ITmkmY~?z5PbZ~|~q901;LXaL3o z|M&s=b7j{%a6Lilr!fK0!v=uh8je8tMhBlc8a(EPBeZp0`uQlfX#=TnOw4{yB+?+fd za4zl}9IEuZTdB(iK(-M8wQnq|ZU6dTSiU&@LJFt*b;8-9h&Wf5pW3BNlG}An0hl-e zz=1VFRC9(k;^z}jh3a)=CxK!h(@{p zTt@q8&JtReei=CmfYurrc7rXOjpT_XxUbF Date: Thu, 29 Aug 2024 13:41:20 +0200 Subject: [PATCH 04/82] Use `Parser.Input#fromString()` in `RewriteTest` The idea is to get the `synthetic` flag to be set to `true`. --- .../src/main/java/org/openrewrite/test/RewriteTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java b/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java index 201f0a94712..2560c78f508 100644 --- a/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java +++ b/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java @@ -266,7 +266,7 @@ default void rewriteRun(Consumer spec, SourceSpec... sourceSpecs) for (UncheckedConsumer> consumer : testClassSpec.allSources) { consumer.accept(sourceSpec); } - inputs.put(sourceSpec, new Parser.Input(sourcePath, () -> new ByteArrayInputStream(beforeTrimmed.getBytes(parser.getCharset(ctx))))); + inputs.put(sourceSpec, Parser.Input.fromString(sourcePath, beforeTrimmed, parser.getCharset(ctx))); } Path relativeTo = testMethodSpec.relativeTo == null ? testClassSpec.relativeTo : testMethodSpec.relativeTo; From 758bbd8201c37fda503632fcac2f6547e7169e41 Mon Sep 17 00:00:00 2001 From: Peter Streef Date: Thu, 29 Aug 2024 15:50:10 +0200 Subject: [PATCH 05/82] Force lowercase urls and origins in `GitRemote` (#4456) --- .../src/main/java/org/openrewrite/GitRemote.java | 13 +++++++++---- .../test/java/org/openrewrite/GitRemoteTest.java | 15 +++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java index dd85812bd7a..fa9f6b06d41 100644 --- a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java +++ b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java @@ -231,7 +231,8 @@ private String repositoryPath(RemoteServerMatch match, URI normalizedUri) { private static final Pattern PORT_PATTERN = Pattern.compile(":\\d+"); - static URI normalize(String url) { + static URI normalize(String original) { + String url = original.toLowerCase(Locale.ENGLISH); try { URIish uri = new URIish(url); String scheme = uri.getScheme(); @@ -266,7 +267,7 @@ static URI normalize(String url) { String path = uri.getPath().replaceFirst("/$", "") .replaceFirst("\\.git$", "") .replaceFirst("^/", ""); - return URI.create((scheme + "://" + host + maybePort + "/" + path).replaceFirst("/$", "")); + return URI.create((scheme + "://" + host + maybePort + "/" + path).replaceFirst("/$", "").toLowerCase(Locale.ENGLISH)); } catch (URISyntaxException e) { throw new IllegalStateException("Unable to parse origin from: " + url, e); } @@ -305,8 +306,12 @@ public RemoteServer(Service service, String origin, URI... uris) { public RemoteServer(Service service, String origin, Collection uris) { this.service = service; - this.origin = origin; - this.uris.addAll(uris); + this.origin = origin.toLowerCase(Locale.ENGLISH); + this.uris.addAll(uris.stream() + .map(URI::toString) + .map(urlString -> urlString.toLowerCase(Locale.ENGLISH)) + .map(URI::create) + .collect(Collectors.toList())); } private GitRemote.Parser.@Nullable RemoteServerMatch match(URI normalizedUri) { diff --git a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java index 2fb0991b2b1..013cd8d965a 100644 --- a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java @@ -39,13 +39,16 @@ public class GitRemoteTest { git@gitlab.com:group/subgroup/subergroup/subestgroup/repo.git, gitlab.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo ssh://git@gitlab.com:22/group/subgroup/subergroup/subestgroup/repo.git, gitlab.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo - https://bitbucket.org/PRJ/repo, bitbucket.org, PRJ/repo, PRJ, repo - git@bitbucket.org:PRJ/repo.git, bitbucket.org, PRJ/repo, PRJ, repo - ssh://bitbucket.org/PRJ/repo.git, bitbucket.org, PRJ/repo, PRJ, repo + https://bitbucket.org/PRJ/repo, bitbucket.org, prj/repo, prj, repo + git@bitbucket.org:PRJ/repo.git, bitbucket.org, prj/repo, prj, repo + ssh://bitbucket.org/PRJ/repo.git, bitbucket.org, prj/repo, prj, repo https://org@dev.azure.com/org/project/_git/repo, dev.azure.com, org/project/repo, org/project, repo https://dev.azure.com/org/project/_git/repo, dev.azure.com, org/project/repo, org/project, repo git@ssh.dev.azure.com:v3/org/project/repo, dev.azure.com, org/project/repo, org/project, repo + + HTTPS://GITHUB.COM/ORG/REPO.GIT, github.com, org/repo, org, repo + HtTpS://GitHub.CoM/OrG/R3P0.GIT, github.com, org/r3p0, org, r3p0 """) void parseKnownRemotes(String cloneUrl, String expectedOrigin, String expectedPath, String expectedOrganization, String expectedRepositoryName) { GitRemote.Parser parser = new GitRemote.Parser(); @@ -83,9 +86,9 @@ void parseUnknownRemote(String cloneUrl, String expectedOrigin, String expectedP https://scm.company.com:443/stash/scm/org/repo.git, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo https://scm.company.com:1234/stash/scm/org/repo.git, https://scm.company.com:1234/stash, Bitbucket, scm.company.com:1234/stash, org/repo, org, repo git@scm.company.com:stash/org/repo.git, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo - ssh://scm.company.com/stash/org/repo, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo + ssh://scm.CompanY.com/stash/org/repo, scm.cOMpAnY.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo ssh://scm.company.com:22/stash/org/repo, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo - ssh://scm.company.com:7999/stash/org/repo, ssh://scm.company.com:7999/stash, Bitbucket, scm.company.com:7999/stash, org/repo, org, repo + ssh://scm.company.com:7999/stash/org/repo, ssh://sCm.company.com:7999/stash, Bitbucket, scm.company.com:7999/stash, org/repo, org, repo https://scm.company.com/very/long/context/path/org/repo.git, scm.company.com/very/long/context/path, Bitbucket, scm.company.com/very/long/context/path, org/repo, org, repo https://scm.company.com:1234/very/long/context/path/org/repo.git, https://scm.company.com:1234/very/long/context/path, Bitbucket, scm.company.com:1234/very/long/context/path, org/repo, org, repo @@ -97,7 +100,7 @@ void parseUnknownRemote(String cloneUrl, String expectedOrigin, String expectedP git@scm.company.com:group/subgroup/subergroup/subestgroup/repo.git, ssh://scm.company.com, GitLab, scm.company.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo https://scm.company.com:443/group/subgroup/subergroup/subestgroup/repo.git, scm.company.com, GitLab, scm.company.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo ssh://scm.company.com:22/group/subgroup/subergroup/subestgroup/repo.git, ssh://scm.company.com, GitLab, scm.company.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo - ssh://scm.company.com:222/group/subgroup/subergroup/subestgroup/repo.git, ssh://scm.company.com:222, GitLab, scm.company.com:222, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo + ssh://SCM.COMPANY.COM:222/group/subgroup/subergroup/subestgroup/repo.git, ssh://SCM.CoMpAnY.cOm:222, GitLab, scm.company.com:222, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo https://scm.company.com/very/long/context/path/group/subgroup/subergroup/subestgroup/repo, scm.company.com/very/long/context/path, GitLab, scm.company.com/very/long/context/path, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo """) From 2bffa3dac7cdb0d1f5c6ffb41a032a5621da7d1e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 29 Aug 2024 17:36:33 +0000 Subject: [PATCH 06/82] LICENSE using Apache License Version 2.0 Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.text.CreateTextFile?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- LICENSE | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 8ff3b85febc..261eeb9e9f8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -178,15 +178,15 @@ Apache License APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate - comment syntax for the file formatPrefix. We also recommend that a + comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From b23e25630902dafc8321752eacb0cdec283079fd Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 29 Aug 2024 14:41:43 -0700 Subject: [PATCH 07/82] Fix incomplete boolean simplification of ternary operators. --- .../SimplifyBooleanExpressionVisitorTest.java | 56 +++++++++++++------ .../SimplifyBooleanExpressionVisitor.java | 16 ++---- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java index b72119b2b19..911de9f7dd9 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java @@ -458,38 +458,60 @@ public class A { } @Test - void negatedTernary() { + void ternaryDoubleNegation() { + rewriteRun( + //language=java + java(""" + class A { + boolean orElse(Boolean nullable, boolean nonnull) { + return !(nullable != null ? nullable : nonnull); + } + } + """, + """ + class A { + boolean orElse(Boolean nullable, boolean nonnull) { + return nullable == null ? nonnull : nullable; + } + } + """ + ) + ); + } + + @Test + void ternayNegation() { rewriteRun( java( """ public class A { - boolean m1(boolean a) { - return !(a ? true : false); + boolean m1(boolean a, boolean b, boolean c) { + return !(a ? b : c); } - boolean m2(boolean a) { - return !(!a ? true : false); + boolean m2(boolean a, boolean b, boolean c) { + return !(!a ? b : c); } - boolean m3(boolean a) { - return !a ? true : false; + boolean m3(boolean a, boolean b, boolean c) { + return !a ? b : c; } - boolean m4(boolean a) { - return a ? true : false; + boolean m4(boolean a, boolean b, boolean c) { + return a ? b : c; } } """, """ public class A { - boolean m1(boolean a) { - return a ? false : true; + boolean m1(boolean a, boolean b, boolean c) { + return a ? c : b; } - boolean m2(boolean a) { - return a ? true : false; + boolean m2(boolean a, boolean b, boolean c) { + return a ? b : c; } - boolean m3(boolean a) { - return a ? false : true; + boolean m3(boolean a, boolean b, boolean c) { + return a ? c : b; } - boolean m4(boolean a) { - return a ? true : false; + boolean m4(boolean a, boolean b, boolean c) { + return a ? b : c; } } """ diff --git a/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java index c84535285c4..50808128be1 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java @@ -171,17 +171,11 @@ private Expression unpackExpression(Expression expr, Expression j) { } } else if (parenthesized instanceof J.Ternary) { J.Ternary ternary = (J.Ternary) parenthesized; - Expression negatedCondition = maybeNegate(ternary.getCondition()); - if (negatedCondition != ternary.getCondition()) { - j = ternary - .withCondition(negatedCondition) - .withPrefix(j.getPrefix()); - } else { - j = ternary - .withTruePart(ternary.getFalsePart()) - .withFalsePart(ternary.getTruePart()) - .withPrefix(j.getPrefix()); - } + j = ternary + .withCondition(maybeNegate(ternary.getCondition())) + .withTruePart(ternary.getFalsePart()) + .withFalsePart(ternary.getTruePart()) + .withPrefix(j.getPrefix()); } else if (parenthesized instanceof Expression) { j = unpackExpression((Expression) parenthesized, j); } From 802f4b5f0b3c3d66ae4c14af07d2131466f777b9 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 30 Aug 2024 11:50:33 +0000 Subject: [PATCH 08/82] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../src/main/java/org/openrewrite/CreateFileVisitor.java | 2 +- .../main/java/org/openrewrite/internal/StringUtils.java | 2 +- .../openrewrite/marker/OperatingSystemProvenance.java | 2 +- .../main/java/org/openrewrite/text/FindAndReplace.java | 2 +- .../org/openrewrite/config/DeclarativeRecipeTest.java | 2 +- .../org/openrewrite/gradle/UpdateGradleWrapperTest.java | 9 +++++++-- .../org/openrewrite/gradle/util/GradleWrapperTest.java | 2 +- .../java/org/openrewrite/groovy/GroovyParserVisitor.java | 4 ++-- .../org/openrewrite/hcl/internal/HclParserVisitor.java | 2 +- .../java/org/openrewrite/java/MethodMatcherTest.java | 1 - .../org/openrewrite/java/search/FindMethodsTest.java | 2 -- .../org/openrewrite/java/ChangeStaticFieldToMethod.java | 2 +- .../org/openrewrite/java/RemoveAnnotationVisitor.java | 2 +- .../java/ShortenFullyQualifiedTypeReferences.java | 2 +- .../java/format/NormalizeTabsOrSpacesVisitor.java | 2 +- .../java/org/openrewrite/java/style/EmptyBlockStyle.java | 2 +- .../org/openrewrite/java/style/OperatorWrapStyle.java | 2 +- .../src/main/java/org/openrewrite/java/tree/J.java | 2 +- .../main/java/org/openrewrite/maven/internal/RawPom.java | 2 +- .../xml/format/NormalizeTabsOrSpacesVisitor.java | 2 +- 20 files changed, 25 insertions(+), 23 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/CreateFileVisitor.java b/rewrite-core/src/main/java/org/openrewrite/CreateFileVisitor.java index 5afc72f5cdd..8a295913529 100644 --- a/rewrite-core/src/main/java/org/openrewrite/CreateFileVisitor.java +++ b/rewrite-core/src/main/java/org/openrewrite/CreateFileVisitor.java @@ -17,8 +17,8 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.openrewrite.internal.lang.NonNull; import java.nio.file.Path; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java b/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java index 476cae952cd..bdeaaa07585 100644 --- a/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java +++ b/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java @@ -15,8 +15,8 @@ */ package org.openrewrite.internal; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.openrewrite.internal.lang.NonNull; import java.io.ByteArrayOutputStream; import java.io.File; diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/OperatingSystemProvenance.java b/rewrite-core/src/main/java/org/openrewrite/marker/OperatingSystemProvenance.java index 59a0813b7d8..ac5c3cad193 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/OperatingSystemProvenance.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/OperatingSystemProvenance.java @@ -22,9 +22,9 @@ import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.With; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; -import org.openrewrite.internal.lang.NonNull; import java.io.File; import java.nio.file.Files; diff --git a/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java b/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java index 27b61da5f0e..0842da78a57 100644 --- a/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java +++ b/rewrite-core/src/main/java/org/openrewrite/text/FindAndReplace.java @@ -17,10 +17,10 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.binary.Binary; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.marker.AlreadyReplaced; import org.openrewrite.marker.Marker; import org.openrewrite.marker.SearchResult; diff --git a/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java b/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java index 3ef4da16d61..a70b9c7701d 100644 --- a/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java @@ -17,10 +17,10 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.openrewrite.*; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.marker.SearchResult; import org.openrewrite.test.RewriteTest; import org.openrewrite.text.ChangeText; diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java index dd798643531..482ad8cf782 100755 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpdateGradleWrapperTest.java @@ -43,9 +43,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.gradle.Assertions.buildGradle; import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; -import static org.openrewrite.gradle.util.GradleWrapper.*; +import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_BATCH_LOCATION; +import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_JAR_LOCATION; +import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_PROPERTIES_LOCATION; +import static org.openrewrite.gradle.util.GradleWrapper.WRAPPER_SCRIPT_LOCATION; import static org.openrewrite.properties.Assertions.properties; -import static org.openrewrite.test.SourceSpecs.*; +import static org.openrewrite.test.SourceSpecs.dir; +import static org.openrewrite.test.SourceSpecs.other; +import static org.openrewrite.test.SourceSpecs.text; @SuppressWarnings("UnusedProperty") class UpdateGradleWrapperTest implements RewriteTest { diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/util/GradleWrapperTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/util/GradleWrapperTest.java index 1487503fbae..21d47b90b5e 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/util/GradleWrapperTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/util/GradleWrapperTest.java @@ -21,8 +21,8 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import org.jspecify.annotations.NonNull; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.NonNull; import java.io.IOException; import java.io.InputStream; diff --git a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java index 39a2f4aaebe..8060e68c238 100644 --- a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java +++ b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java @@ -25,6 +25,7 @@ import org.codehaus.groovy.ast.stmt.*; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.transform.stc.StaticTypesMarker; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; @@ -33,13 +34,12 @@ import org.openrewrite.groovy.tree.G; import org.openrewrite.internal.EncodingDetectingInputStream; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.internal.JavaTypeCache; import org.openrewrite.java.marker.ImplicitReturn; import org.openrewrite.java.marker.Semicolon; +import org.openrewrite.java.tree.*; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.Statement; -import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import java.math.BigDecimal; diff --git a/rewrite-hcl/src/main/java/org/openrewrite/hcl/internal/HclParserVisitor.java b/rewrite-hcl/src/main/java/org/openrewrite/hcl/internal/HclParserVisitor.java index a11740bb805..c8c221a8ad6 100644 --- a/rewrite-hcl/src/main/java/org/openrewrite/hcl/internal/HclParserVisitor.java +++ b/rewrite-hcl/src/main/java/org/openrewrite/hcl/internal/HclParserVisitor.java @@ -19,12 +19,12 @@ import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.FileAttributes; import org.openrewrite.hcl.internal.grammar.HCLParser; import org.openrewrite.hcl.internal.grammar.HCLParserBaseVisitor; import org.openrewrite.hcl.tree.*; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.marker.Markers; import java.nio.charset.Charset; diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/MethodMatcherTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/MethodMatcherTest.java index 7854534c856..4a64ee9a39d 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/MethodMatcherTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/MethodMatcherTest.java @@ -15,7 +15,6 @@ */ package org.openrewrite.java; -import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindMethodsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindMethodsTest.java index ea1aa678ce3..a3161dda572 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindMethodsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindMethodsTest.java @@ -18,9 +18,7 @@ import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; import org.openrewrite.DocumentExample; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.table.MethodCalls; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java index ac8393d78c4..7f83e218494 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java @@ -19,9 +19,9 @@ import lombok.Value; import org.apache.commons.lang3.StringUtils; import org.intellij.lang.annotations.Language; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.search.UsesField; import org.openrewrite.java.tree.*; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/RemoveAnnotationVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/RemoveAnnotationVisitor.java index 6e9056ae749..ecba536f14b 100755 --- a/rewrite-java/src/main/java/org/openrewrite/java/RemoveAnnotationVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/RemoveAnnotationVisitor.java @@ -17,9 +17,9 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.NonNull; import org.openrewrite.ExecutionContext; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.tree.*; import java.util.ArrayList; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java b/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java index 5453fcc4fb8..d8907ed52c9 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java @@ -15,10 +15,10 @@ */ package org.openrewrite.java; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.internal.DefaultJavaTypeSignatureBuilder; import org.openrewrite.java.service.ImportService; import org.openrewrite.java.tree.*; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/format/NormalizeTabsOrSpacesVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/format/NormalizeTabsOrSpacesVisitor.java index 947eca54671..9ae6fdd6e01 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/format/NormalizeTabsOrSpacesVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/format/NormalizeTabsOrSpacesVisitor.java @@ -15,11 +15,11 @@ */ package org.openrewrite.java.format; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.JavadocVisitor; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java index 8a0e4e9b0fc..bd808cc7d50 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java @@ -17,7 +17,7 @@ import lombok.Value; import lombok.With; -import org.openrewrite.internal.lang.NonNull; +import org.jspecify.annotations.NonNull; import org.openrewrite.internal.lang.NullFields; import org.openrewrite.style.Style; import org.openrewrite.style.StyleHelper; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java index 4bd3006ecbf..cc88978e76d 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java @@ -17,7 +17,7 @@ import lombok.Value; import lombok.With; -import org.openrewrite.internal.lang.NonNull; +import org.jspecify.annotations.NonNull; import org.openrewrite.internal.lang.NullFields; import org.openrewrite.style.Style; import org.openrewrite.style.StyleHelper; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java b/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java index acc5487e4c3..c8aa15fafa1 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java @@ -19,13 +19,13 @@ import lombok.*; import lombok.experimental.FieldDefaults; import lombok.experimental.NonFinal; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.LoathingOfOthers; import org.openrewrite.internal.SelfLoathing; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.JavaPrinter; import org.openrewrite.java.JavaTypeVisitor; import org.openrewrite.java.JavaVisitor; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java index 5415065df25..4befdcfd431 100755 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java @@ -23,9 +23,9 @@ import lombok.*; import lombok.experimental.FieldDefaults; import lombok.experimental.NonFinal; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.maven.tree.*; import javax.xml.bind.annotation.XmlRootElement; diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/format/NormalizeTabsOrSpacesVisitor.java b/rewrite-xml/src/main/java/org/openrewrite/xml/format/NormalizeTabsOrSpacesVisitor.java index c8625492492..3251f773774 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/format/NormalizeTabsOrSpacesVisitor.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/format/NormalizeTabsOrSpacesVisitor.java @@ -15,10 +15,10 @@ */ package org.openrewrite.xml.format; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.xml.XmlIsoVisitor; import org.openrewrite.xml.style.TabsAndIndentsStyle; import org.openrewrite.xml.tree.Xml; From 0805fefab5526d9741597233a0433e30d33055b2 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 30 Aug 2024 14:32:05 +0200 Subject: [PATCH 09/82] Revert JSpecify changes to style classes over unexplained failures --- .../main/java/org/openrewrite/java/style/EmptyBlockStyle.java | 2 +- .../main/java/org/openrewrite/java/style/OperatorWrapStyle.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java index bd808cc7d50..8a0e4e9b0fc 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/EmptyBlockStyle.java @@ -17,7 +17,7 @@ import lombok.Value; import lombok.With; -import org.jspecify.annotations.NonNull; +import org.openrewrite.internal.lang.NonNull; import org.openrewrite.internal.lang.NullFields; import org.openrewrite.style.Style; import org.openrewrite.style.StyleHelper; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java index cc88978e76d..4bd3006ecbf 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/OperatorWrapStyle.java @@ -17,7 +17,7 @@ import lombok.Value; import lombok.With; -import org.jspecify.annotations.NonNull; +import org.openrewrite.internal.lang.NonNull; import org.openrewrite.internal.lang.NullFields; import org.openrewrite.style.Style; import org.openrewrite.style.StyleHelper; From 0dd5c4bbc15cbf138ef0d138e9f3c5d7fc09bc84 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 30 Aug 2024 18:31:22 +0200 Subject: [PATCH 10/82] Apply OpenRewrite best practice, as shown on PRs --- .../cleanup/SimplifyBooleanExpressionVisitorTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java index 911de9f7dd9..a179a4621db 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java @@ -461,7 +461,8 @@ public class A { void ternaryDoubleNegation() { rewriteRun( //language=java - java(""" + java( + """ class A { boolean orElse(Boolean nullable, boolean nonnull) { return !(nullable != null ? nullable : nonnull); @@ -598,7 +599,7 @@ boolean booleanExpression() { a == null || !a.isEmpty() // a == null || !a.isEmpty() a != null && a.isEmpty() // a != null && a.isEmpty() a != null && !a.isEmpty() // a != null && !a.isEmpty() - + "" == null || "".isEmpty() // true "" == null || !"".isEmpty() // false "" != null && "".isEmpty() // true @@ -608,7 +609,7 @@ boolean booleanExpression() { "b" == null || !"b".isEmpty() // true "b" != null && "b".isEmpty() // false "b" != null && !"b".isEmpty() // true - + a == null || a.isEmpty() || "" == null || "".isEmpty() // true a == null || a.isEmpty() || "" == null || !"".isEmpty() // a == null || a.isEmpty() a == null || a.isEmpty() || "" != null && "".isEmpty() // true @@ -625,7 +626,7 @@ boolean booleanExpression() { a == null || !a.isEmpty() && "" == null || !"".isEmpty() // a == null a == null || !a.isEmpty() && "" != null && "".isEmpty() // a == null || !a.isEmpty() a == null || !a.isEmpty() && "" != null && !"".isEmpty() // a == null - + a == null || a.isEmpty() || "b" == null || "b".isEmpty() // a == null || a.isEmpty() a == null || a.isEmpty() || "b" == null || !"b".isEmpty() // true a == null || a.isEmpty() || "b" != null && "b".isEmpty() // a == null || a.isEmpty() From a593ef62d84d7aeac96b5f3fe13694cae6740229 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Fri, 30 Aug 2024 19:27:46 +0200 Subject: [PATCH 11/82] `ChangeType` should fully qualify type usage in the case of conflicting imports (#4458) * Add initial testcase that shows the problem * Check whether an import becomes ambiguous and if it does start fully qualifying references * Reduce warnings shown in IDE --------- Co-authored-by: Laurens Westerlaken Co-authored-by: Tim te Beek --- .../org/openrewrite/java/ChangeTypeTest.java | 320 ++++++++++-------- .../java/org/openrewrite/java/ChangeType.java | 51 ++- 2 files changed, 212 insertions(+), 159 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java index 5d878133206..f7deeb632b6 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java @@ -101,7 +101,7 @@ void starImport() { java( """ import java.util.logging.*; - + class Test { static void method() { LoggingMXBean loggingBean = null; @@ -111,7 +111,7 @@ static void method() { """ import java.lang.management.PlatformLoggingMXBean; import java.util.logging.*; - + class Test { static void method() { PlatformLoggingMXBean loggingBean = null; @@ -130,7 +130,7 @@ void allowJavaLangSubpackages() { java( """ import java.util.logging.LoggingMXBean; - + class Test { static void method() { LoggingMXBean loggingBean = null; @@ -139,7 +139,7 @@ static void method() { """, """ import java.lang.management.PlatformLoggingMXBean; - + class Test { static void method() { PlatformLoggingMXBean loggingBean = null; @@ -159,7 +159,7 @@ void unnecessaryImport() { java( """ import test.Outer; - + class Test { private Outer p = Outer.of(); private Outer p2 = test.Outer.of(); @@ -169,12 +169,12 @@ class Test { java( """ package test; - + public class Outer { public static Outer of() { return new Outer(); } - + public static class Inner { } } @@ -193,7 +193,7 @@ void changeInnerClassToOuterClass() { """ import java.util.Map; import java.util.Map.Entry; - + class Test { Entry p; Map.Entry p2; @@ -202,7 +202,7 @@ class Test { """, """ import java.util.List; - + class Test { List p; List p2; @@ -221,14 +221,14 @@ void changeStaticFieldAccess() { java( """ import java.io.File; - + class Test { String p = File.separator; } """, """ import my.pkg.List; - + class Test { String p = List.separator; } @@ -253,14 +253,14 @@ void replaceWithNestedType() { java( """ import java.io.File; - + class Test { File p; } """, """ import java.util.Map; - + class Test { Map.Entry p; } @@ -277,14 +277,14 @@ void replacePrivateNestedType() { java( """ package a; - + class A { private static class B1 {} } """, """ package a; - + class A { private static class B2 {} } @@ -301,7 +301,7 @@ void deeplyNestedInnerClass() { java( """ package a; - + class A { public static class B { public static class C { @@ -311,7 +311,7 @@ public static class C { """, """ package a; - + class A { public static class B { public static class C2 { @@ -329,12 +329,12 @@ void simpleName() { java( """ import a.A1; - + public class B extends A1 {} """, """ import a.A2; - + public class B extends A2 {} """ ), @@ -375,7 +375,7 @@ void array2() { java( """ package com.acme.product; - + public class Pojo { } """ @@ -383,12 +383,12 @@ public class Pojo { java( """ package com.acme.project.impl; - + import com.acme.product.Pojo; - + public class UsePojo2 { Pojo[] p; - + void run() { p[0] = null; } @@ -396,12 +396,12 @@ void run() { """, """ package com.acme.project.impl; - + import com.acme.product.v2.Pojo; - + public class UsePojo2 { Pojo[] p; - + void run() { p[0] = null; } @@ -420,14 +420,14 @@ void array() { java( """ import a.A1; - + public class B { A1[] a = new A1[0]; } """, """ import a.A2; - + public class B { A2[] a = new A2[0]; } @@ -444,14 +444,14 @@ void multiDimensionalArray() { java( """ import a.A1; - + public class A { A1[][] multiDimensionalArray; } """, """ import a.A2; - + public class A { A2[][] multiDimensionalArray; } @@ -478,12 +478,12 @@ void classDecl() { java( """ import a.A1; - + public class B extends A1 implements I1 {} """, """ import a.A2; - + public class B extends A2 implements I2 {} """ ) @@ -499,14 +499,14 @@ void method() { java( """ import a.A1; - + public class B { public A1 foo() throws A1 { return null; } } """, """ import a.A2; - + public class B { public A2 foo() throws A2 { return null; } } @@ -523,10 +523,10 @@ void methodInvocationTypeParametersAndWildcard() { java( """ import a.A1; - + public class B { public T generic(T n, java.util.List in) { - + } public void test() { A1.stat(); @@ -536,10 +536,10 @@ public void test() { """, """ import a.A2; - + public class B { public T generic(T n, java.util.List in) { - + } public void test() { A2.stat(); @@ -560,7 +560,7 @@ void multiCatch() { java( """ import a.A1; - + public class B { public void test() { try {} @@ -570,7 +570,7 @@ public void test() { """, """ import a.A2; - + public class B { public void test() { try {} @@ -590,14 +590,14 @@ void multiVariable() { java( """ import a.A1; - + public class B { A1 f1, f2; } """, """ import a.A2; - + public class B { A2 f1, f2; } @@ -614,14 +614,14 @@ void newClass() { java( """ import a.A1; - + public class B { A1 a = new A1(); } """, """ import a.A2; - + public class B { A2 a = new A2(); } @@ -641,7 +641,7 @@ void updateAssignments() { java( """ import a.A1; - + class B { void method(A1 param) { A1 a = param; @@ -650,7 +650,7 @@ void method(A1 param) { """, """ import a.A2; - + class B { void method(A2 param) { A2 a = param; @@ -669,18 +669,18 @@ void parameterizedType() { java( """ import a.A1; - + import java.util.Map; - + public class B { Map m; } """, """ import a.A2; - + import java.util.Map; - + public class B { Map m; } @@ -698,14 +698,14 @@ void typeCast() { java( """ import a.A1; - + public class B { A1 a = (A1) null; } """, """ import a.A2; - + public class B { A2 a = (A2) null; } @@ -722,14 +722,14 @@ void classReference() { java( """ import a.A1; - + public class A { Class clazz = A1.class; } """, """ import a.A2; - + public class A { Class clazz = A2.class; } @@ -746,7 +746,7 @@ void methodSelect() { java( """ import a.A1; - + public class B { A1 a = null; public void test() { a.foo(); } @@ -754,7 +754,7 @@ public class B { """, """ import a.A2; - + public class B { A2 a = null; public void test() { a.foo(); } @@ -773,7 +773,7 @@ void staticImport() { java( """ import static a.A1.stat; - + public class B { public void test() { stat(); @@ -782,7 +782,7 @@ public void test() { """, """ import static a.A2.stat; - + public class B { public void test() { stat(); @@ -799,7 +799,7 @@ void staticImports2() { java( """ package com.acme.product; - + public class RunnableFactory { public static String getString() { return "hello"; @@ -810,9 +810,9 @@ public static String getString() { java( """ package com.acme.project.impl; - + import static com.acme.product.RunnableFactory.getString; - + public class StaticImportWorker { public void work() { getString().toLowerCase(); @@ -821,9 +821,9 @@ public void work() { """, """ package com.acme.project.impl; - + import static com.acme.product.v2.RunnableFactory.getString; - + public class StaticImportWorker { public void work() { getString().toLowerCase(); @@ -841,7 +841,7 @@ void staticConstant() { java( """ package com.acme.product; - + public class RunnableFactory { public static final String CONSTANT = "hello"; } @@ -850,9 +850,9 @@ public class RunnableFactory { java( """ package com.acme.project.impl; - + import static com.acme.product.RunnableFactory.CONSTANT; - + public class StaticImportWorker { public void work() { System.out.println(CONSTANT + " fred."); @@ -861,9 +861,9 @@ public void work() { """, """ package com.acme.project.impl; - + import static com.acme.product.v2.RunnableFactory.CONSTANT; - + public class StaticImportWorker { public void work() { System.out.println(CONSTANT + " fred."); @@ -945,23 +945,23 @@ public class B {} java( """ package com.myorg; - + import java.util.ArrayList; import com.yourorg.a.A; import java.util.List; - + public class Foo { List a = new ArrayList<>(); } """, """ package com.myorg; - + import com.myorg.b.B; - + import java.util.ArrayList; import java.util.List; - + public class Foo { List a = new ArrayList<>(); } @@ -977,10 +977,10 @@ void changeTypeWithInnerClass() { java( """ package com.acme.product; - + public class OuterClass { public static class InnerClass { - + } } """ @@ -988,15 +988,15 @@ public static class InnerClass { java( """ package de; - + import com.acme.product.OuterClass; import com.acme.product.OuterClass.InnerClass; - + public class UseInnerClass { public String work() { return new InnerClass().toString(); } - + public String work2() { return new OuterClass().toString(); } @@ -1004,15 +1004,15 @@ public String work2() { """, """ package de; - + import com.acme.product.v2.OuterClass; import com.acme.product.v2.OuterClass.InnerClass; - + public class UseInnerClass { public String work() { return new InnerClass().toString(); } - + public String work2() { return new OuterClass().toString(); } @@ -1030,7 +1030,7 @@ void uppercaseInPackage() { java( """ package com.acme.product.util.accessDecision; - + public enum AccessVote { ABSTAIN } @@ -1039,9 +1039,9 @@ public enum AccessVote { java( """ package de; - + import com.acme.product.util.accessDecision.AccessVote; - + public class ProjectVoter { public AccessVote vote() { return AccessVote.ABSTAIN; @@ -1050,9 +1050,9 @@ public AccessVote vote() { """, """ package de; - + import com.acme.product.v2.util.accessDecision.AccessVote; - + public class ProjectVoter { public AccessVote vote() { return AccessVote.ABSTAIN; @@ -1079,7 +1079,7 @@ public interface Procedure { java( """ import com.acme.product.Procedure; - + public abstract class Worker { void callWorker() { worker(() -> { @@ -1090,7 +1090,7 @@ void callWorker() { """, """ import com.acme.product.Procedure2; - + public abstract class Worker { void callWorker() { worker(() -> { @@ -1111,7 +1111,7 @@ void assignment() { java( """ package com.acme.product.util.accessDecision; - + public enum AccessVote { ABSTAIN, GRANT @@ -1121,9 +1121,9 @@ public enum AccessVote { java( """ package de; - + import com.acme.product.util.accessDecision.AccessVote; - + public class ProjectVoter { public AccessVote vote(Object input) { AccessVote fred; @@ -1134,9 +1134,9 @@ public AccessVote vote(Object input) { """, """ package de; - + import com.acme.product.v2.util.accessDecision.AccessVote; - + public class ProjectVoter { public AccessVote vote(Object input) { AccessVote fred; @@ -1157,7 +1157,7 @@ void ternary() { java( """ package com.acme.product.util.accessDecision; - + public enum AccessVote { ABSTAIN, GRANT @@ -1167,9 +1167,9 @@ public enum AccessVote { java( """ package de; - + import com.acme.product.util.accessDecision.AccessVote; - + public class ProjectVoter { public AccessVote vote(Object input) { return input == null ? AccessVote.GRANT : AccessVote.ABSTAIN; @@ -1178,9 +1178,9 @@ public AccessVote vote(Object input) { """, """ package de; - + import com.acme.product.v2.util.accessDecision.AccessVote; - + public class ProjectVoter { public AccessVote vote(Object input) { return input == null ? AccessVote.GRANT : AccessVote.ABSTAIN; @@ -1230,7 +1230,7 @@ void javadocs() { java( """ import java.util.List; - + /** * {@link List} here */ @@ -1240,7 +1240,7 @@ class Test { """, """ import java.util.Collection; - + /** * {@link Collection} here */ @@ -1260,7 +1260,7 @@ void onlyUpdateApplicableImport() { java( """ package com.acme.product.factory; - + public class V1Factory { public static String getItem() { return "V1Factory"; @@ -1271,7 +1271,7 @@ public static String getItem() { java( """ package com.acme.product.factory; - + public class V2Factory { public static String getItem() { return "V2Factory"; @@ -1282,16 +1282,16 @@ public static String getItem() { java( """ import com.acme.product.factory.V1Factory; - + import static com.acme.product.factory.V2Factory.getItem; - + public class UseFactories { static class MyV1Factory extends V1Factory { static String getMyItemInherited() { return getItem(); } } - + static String getMyItemStaticImport() { return getItem(); } @@ -1299,16 +1299,16 @@ static String getMyItemStaticImport() { """, """ import com.acme.product.factory.V1FactoryA; - + import static com.acme.product.factory.V2Factory.getItem; - + public class UseFactories { static class MyV1Factory extends V1FactoryA { static String getMyItemInherited() { return getItem(); } } - + static String getMyItemStaticImport() { return getItem(); } @@ -1391,15 +1391,15 @@ void updateImportPrefixWithEmptyPackage() { java( """ package a.b; - + import java.util.List; - + class Original { } """, """ import java.util.List; - + class Target { } """ @@ -1415,7 +1415,7 @@ void updateClassPrefixWithEmptyPackage() { java( """ package a.b; - + class Original { } """, @@ -1535,9 +1535,9 @@ public class A2 { java( """ package org.foo; - + import a.A1; - + public class Example { public A1 method(A1 a1) { return a1; @@ -1546,9 +1546,9 @@ public A1 method(A1 a1) { """, """ package org.foo; - + import a.A2; - + public class Example { public A2 method(A2 a1) { return a1; @@ -1560,7 +1560,7 @@ public A2 method(A2 a1) { """ import a.A1; import org.foo.Example; - + public class Test { A1 local = new Example().method(null); } @@ -1568,7 +1568,7 @@ public class Test { """ import a.A2; import org.foo.Example; - + public class Test { A2 local = new Example().method(null); } @@ -1596,14 +1596,14 @@ void updateVariableType() { java( """ import a.A1; - + public class Test { A1 a; } """, """ import a.A2; - + public class Test { A2 a; } @@ -1626,7 +1626,7 @@ void boundedGenericType() { java( """ import a.A1; - + public class Test { T method(T t) { return t; @@ -1635,7 +1635,7 @@ T method(T t) { """, """ import a.A2; - + public class Test { T method(T t) { return t; @@ -1658,7 +1658,7 @@ void changeConstructor() { java( """ package a; - + public class A1 { public A1() { } @@ -1666,7 +1666,7 @@ public A1() { """, """ package a; - + public class A2 { public A2() { } @@ -1689,12 +1689,12 @@ void updateJavaTypeClassKindAnnotation() { java( """ package org.openrewrite; - + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Test1 {} @@ -1703,12 +1703,12 @@ void updateJavaTypeClassKindAnnotation() { java( """ package org.openrewrite; - + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; - + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Test2 {} @@ -1717,7 +1717,7 @@ void updateJavaTypeClassKindAnnotation() { java( """ import org.openrewrite.Test1; - + public class A { @Test1 void method() {} @@ -1725,7 +1725,7 @@ void method() {} """, """ import org.openrewrite.Test2; - + public class A { @Test2 void method() {} @@ -1800,14 +1800,13 @@ void doesNotModifyInnerClassesIfIgnoreDefinitionTrue() { spec -> spec.recipe(new ChangeType("Test.InnerA", "Test.InnerB", true)), java( """ - public class Test { private class InnerA { } - + private class InnerB { } - + public void test(String s) { InnerA a = new InnerA(); } @@ -1817,10 +1816,10 @@ public void test(String s) { public class Test { private class InnerA { } - + private class InnerB { } - + public void test(String s) { InnerB a = new InnerB(); } @@ -1851,9 +1850,9 @@ public class Test { java( """ package org.openrewrite; - + import org.openrewrite.Test; - + public class Sibling { public Test test() { return new Test(); @@ -1862,9 +1861,9 @@ public Test test() { """, """ package org.openrewrite; - + import org.openrewrite.subpackage.Test; - + public class Sibling { public Test test() { return new Test(); @@ -1912,9 +1911,9 @@ public ObjectMapper configure(SerializationConfig.Feature f, boolean state) { java( """ import org.codehaus.jackson.map.ObjectMapper; - + import static org.codehaus.jackson.map.SerializationConfig.Feature.WRAP_ROOT_VALUE; - + class A { void test() { ObjectMapper mapper = new ObjectMapper(); @@ -1924,9 +1923,9 @@ void test() { """, """ import com.fasterxml.jackson.databind.ObjectMapper; - + import static com.fasterxml.jackson.databind.SerializationFeature.WRAP_ROOT_VALUE; - + class A { void test() { ObjectMapper mapper = new ObjectMapper(); @@ -1937,4 +1936,39 @@ void test() { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite/issues/4452") + void shouldFullyQualifyWhenNewTypeIsAmbiguous() { + rewriteRun( + spec -> spec.recipe(new ChangeType( + "javax.annotation.Nonnull", + "org.checkerframework.checker.nullness.qual.NonNull", + null)), + // language=java + java( + """ + import lombok.NonNull; + import javax.annotation.Nonnull; + import org.immutables.value.Value; + + @Value.Immutable + @Value.Style(passAnnotations = Nonnull.class) + interface ConflictingImports { + void lombokMethod(@NonNull final String lombokNonNull){} + } + """, + """ + import lombok.NonNull; + import org.immutables.value.Value; + + @Value.Immutable + @Value.Style(passAnnotations = org.checkerframework.checker.nullness.qual.NonNull.class) + interface ConflictingImports { + void lombokMethod(@NonNull final String lombokNonNull){} + } + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java index f6a009a5524..79c1c1277ca 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java @@ -205,9 +205,9 @@ private void addImport(JavaType.FullyQualified owningClass) { if (maybeType instanceof JavaType.FullyQualified) { JavaType.FullyQualified type = (JavaType.FullyQualified) maybeType; if (originalType.getFullyQualifiedName().equals(type.getFullyQualifiedName())) { - sf = (JavaSourceFile) new RemoveImport(originalType.getFullyQualifiedName()).visit(sf, ctx, getCursor().getParentOrThrow()); + sf = (JavaSourceFile) new RemoveImport(originalType.getFullyQualifiedName()).visitNonNull(sf, ctx, getCursor().getParentOrThrow()); } else if (originalType.getOwningClass() != null && originalType.getOwningClass().getFullyQualifiedName().equals(type.getFullyQualifiedName())) { - sf = (JavaSourceFile) new RemoveImport(originalType.getOwningClass().getFullyQualifiedName()).visit(sf, ctx, getCursor().getParentOrThrow()); + sf = (JavaSourceFile) new RemoveImport(originalType.getOwningClass().getFullyQualifiedName()).visitNonNull(sf, ctx, getCursor().getParentOrThrow()); } } } @@ -217,20 +217,18 @@ private void addImport(JavaType.FullyQualified owningClass) { if (fullyQualifiedTarget != null) { JavaType.FullyQualified owningClass = fullyQualifiedTarget.getOwningClass(); if (!topLevelClassnames.contains(getTopLevelClassName(fullyQualifiedTarget).getFullyQualifiedName())) { - if (owningClass != null && !"java.lang".equals(fullyQualifiedTarget.getPackageName())) { - addImport(owningClass); - } - if (!"java.lang".equals(fullyQualifiedTarget.getPackageName())) { - addImport(fullyQualifiedTarget); + if (hasNoConflictingImport(sf)) { + if (owningClass != null && !"java.lang".equals(fullyQualifiedTarget.getPackageName())) { + addImport(owningClass); + } + if (!"java.lang".equals(fullyQualifiedTarget.getPackageName())) { + addImport(fullyQualifiedTarget); + } } } } - if (sf != null) { - sf = sf.withImports(ListUtils.map(sf.getImports(), i -> visitAndCast(i, ctx, super::visitImport))); - } - - j = sf; + j = sf.withImports(ListUtils.map(sf.getImports(), i -> visitAndCast(i, ctx, super::visitImport))); } return j; @@ -301,6 +299,7 @@ public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) { className = originalType.getFullyQualifiedName().substring(iType.getOwningClass().getFullyQualifiedName().length() + 1); } + JavaSourceFile sf = getCursor().firstEnclosing(JavaSourceFile.class); if (ident.getSimpleName().equals(className)) { if (targetType instanceof JavaType.FullyQualified) { if (((JavaType.FullyQualified) targetType).getOwningClass() != null) { @@ -308,7 +307,11 @@ public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) { .withType(null) .withPrefix(ident.getPrefix())); } else { - ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getClassName()); + if (sf != null && hasNoConflictingImport(sf)) { + ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getClassName()); + } else { + ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getFullyQualifiedName()); + } } } else if (targetType instanceof JavaType.Primitive) { ident = ident.withSimpleName(((JavaType.Primitive) targetType).getKeyword()); @@ -316,9 +319,8 @@ public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) { } // Recreate any static imports as needed - JavaSourceFile cu = getCursor().firstEnclosing(JavaSourceFile.class); - if (cu != null) { - for (J.Import anImport : cu.getImports()) { + if (sf != null) { + for (J.Import anImport : sf.getImports()) { if (anImport.isStatic() && anImport.getQualid().getTarget().getType() != null) { JavaType.FullyQualified fqn = TypeUtils.asFullyQualified(anImport.getQualid().getTarget().getType()); if (fqn != null && TypeUtils.isOfClassType(fqn, originalType.getFullyQualifiedName()) && @@ -505,6 +507,23 @@ private Expression updateOuterClassTypes(Expression typeTree) { private boolean isTargetFullyQualifiedType(JavaType.@Nullable FullyQualified fq) { return fq != null && TypeUtils.isOfClassType(fq, originalType.getFullyQualifiedName()) && targetType instanceof JavaType.FullyQualified; } + + private boolean hasNoConflictingImport(JavaSourceFile sf) { + JavaType.FullyQualified oldType = TypeUtils.asFullyQualified(originalType); + JavaType.FullyQualified newType = TypeUtils.asFullyQualified(targetType); + if (oldType == null || newType == null) { + return false; // No way to be sure + } + for (J.Import anImport : sf.getImports()) { + JavaType.FullyQualified currType = TypeUtils.asFullyQualified(anImport.getQualid().getType()); + if (currType != null && + !TypeUtils.isOfType(currType, oldType) && + currType.getClassName().equals(newType.getClassName())) { + return false; + } + } + return true; + } } private static class ChangeClassDefinition extends JavaIsoVisitor { From 441c7c94f01c102a87b8a8c57b3584d48c1880cd Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Mon, 2 Sep 2024 09:52:21 +0200 Subject: [PATCH 12/82] New `AddMethodParameter` recipe (#4460) * New `AddMethodParameter` recipe This recipe can be used to add a new parameter to methods matching the given pattern. The parameter to be added is specified using type, name, and optionally also a position (if none is given it will be appended to the end). --- .../groovy/ChangeTypeAdaptabilityTest.java | 8 +- .../SimplifyBooleanExpressionVisitorTest.java | 7 +- .../openrewrite/java/AddMethodParameter.java | 260 ++++++++++++++++ .../org/openrewrite/java/MethodMatcher.java | 39 +-- .../ShortenFullyQualifiedTypeReferences.java | 11 +- .../openrewrite/java/search/DeclaresType.java | 8 +- .../org/openrewrite/java/tree/TypeUtils.java | 40 +++ .../java/AddMethodParameterTest.java | 281 ++++++++++++++++++ 8 files changed, 603 insertions(+), 51 deletions(-) create mode 100644 rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java create mode 100644 rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java diff --git a/rewrite-groovy/src/test/java/org/openrewrite/groovy/ChangeTypeAdaptabilityTest.java b/rewrite-groovy/src/test/java/org/openrewrite/groovy/ChangeTypeAdaptabilityTest.java index 1575b1ff875..bc6a594143a 100644 --- a/rewrite-groovy/src/test/java/org/openrewrite/groovy/ChangeTypeAdaptabilityTest.java +++ b/rewrite-groovy/src/test/java/org/openrewrite/groovy/ChangeTypeAdaptabilityTest.java @@ -79,13 +79,17 @@ class Original {} ), groovy( """ + import a.b.Original + class A { - a.b.Original type + Original type } """, """ + import x.y.Target + class A { - x.y.Target type + Target type } """ ) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java index a179a4621db..dac111e5458 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitorTest.java @@ -460,7 +460,6 @@ public class A { @Test void ternaryDoubleNegation() { rewriteRun( - //language=java java( """ class A { @@ -599,7 +598,7 @@ boolean booleanExpression() { a == null || !a.isEmpty() // a == null || !a.isEmpty() a != null && a.isEmpty() // a != null && a.isEmpty() a != null && !a.isEmpty() // a != null && !a.isEmpty() - + "" == null || "".isEmpty() // true "" == null || !"".isEmpty() // false "" != null && "".isEmpty() // true @@ -609,7 +608,7 @@ boolean booleanExpression() { "b" == null || !"b".isEmpty() // true "b" != null && "b".isEmpty() // false "b" != null && !"b".isEmpty() // true - + a == null || a.isEmpty() || "" == null || "".isEmpty() // true a == null || a.isEmpty() || "" == null || !"".isEmpty() // a == null || a.isEmpty() a == null || a.isEmpty() || "" != null && "".isEmpty() // true @@ -626,7 +625,7 @@ boolean booleanExpression() { a == null || !a.isEmpty() && "" == null || !"".isEmpty() // a == null a == null || !a.isEmpty() && "" != null && "".isEmpty() // a == null || !a.isEmpty() a == null || !a.isEmpty() && "" != null && !"".isEmpty() // a == null - + a == null || a.isEmpty() || "b" == null || "b".isEmpty() // a == null || a.isEmpty() a == null || a.isEmpty() || "b" == null || !"b".isEmpty() // true a == null || a.isEmpty() || "b" != null && "b".isEmpty() // a == null || a.isEmpty() diff --git a/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java b/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java new file mode 100644 index 00000000000..b180d810512 --- /dev/null +++ b/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java @@ -0,0 +1,260 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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. + */ +package org.openrewrite.java; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.search.DeclaresType; +import org.openrewrite.java.service.ImportService; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.openrewrite.Tree.randomId; + +@Value +@EqualsAndHashCode(callSuper = false) +public class AddMethodParameter extends Recipe { + + /** + * A method pattern that is used to find matching method declarations. + * See {@link MethodMatcher} for details on the expression's syntax. + */ + @Option(displayName = "Method pattern", + description = "A method pattern that is used to find the method declarations to modify.", + example = "com.yourorg.A foo(int, int)") + String methodPattern; + + @Option(displayName = "Parameter type", + description = "The type of the parameter that gets added.", + example = "java.lang.String") + String parameterType; + + @Option(displayName = "Parameter name", + description = "The name of the parameter that gets added.", + example = "name") + String parameterName; + + @Option(displayName = "Parameter index", + description = "A zero-based index that indicates the position at which the parameter will be added. At the end by default.", + example = "0", + required = false) + @Nullable + Integer parameterIndex; + + @Override + public String getInstanceNameSuffix() { + return String.format("`%s %s` in methods `%s`", parameterType, parameterName, methodPattern); + } + + @Override + public String getDisplayName() { + return "Add method parameter to a method declaration"; + } + + @Override + public String getDescription() { + return "Adds a new method parameter to an existing method declaration."; + } + + @Override + public TreeVisitor getVisitor() { + int idx = methodPattern.indexOf('#'); + idx = idx == -1 ? methodPattern.indexOf(' ') : idx; + return Preconditions.check(new DeclaresType<>(methodPattern.substring(0, idx)), new AddNullMethodArgumentVisitor(methodPattern)); + } + + private class AddNullMethodArgumentVisitor extends JavaIsoVisitor { + private final MethodMatcher methodMatcher; + + public AddNullMethodArgumentVisitor(String methodPattern) { + this.methodMatcher = new MethodMatcher(methodPattern); + } + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + method = super.visitMethodDeclaration(method, ctx); + J.ClassDeclaration enclosing = getCursor().firstEnclosing(J.ClassDeclaration.class); + if (enclosing != null && methodMatcher.matches(method, enclosing)) { + for (Statement parameter : method.getParameters()) { + if (parameter instanceof J.VariableDeclarations && ((J.VariableDeclarations) parameter).getVariables().get(0).getSimpleName().equals(parameterName)) { + return method; + } + } + J.VariableDeclarations parameter = createParameter(method); + method = addParameter(method, parameter); + } + return autoFormat(method, ctx); + } + + private J.MethodDeclaration addParameter(J.MethodDeclaration method, J.VariableDeclarations parameter) { + List originalParameters = method.getParameters(); + if (method.getParameters().isEmpty() || method.getParameters().size() == 1 && method.getParameters().get(0) instanceof J.Empty) { + originalParameters = new ArrayList<>(); + } else { + if (parameterIndex == null || parameterIndex != 0) { + parameter = parameter.withPrefix(Space.SINGLE_SPACE); + } else { + originalParameters = ListUtils.mapFirst(originalParameters, p -> p.getPrefix().isEmpty() ? p.withPrefix(Space.SINGLE_SPACE) : p); + } + } + + if (parameterIndex == null) { + method = method.withParameters(ListUtils.concat(originalParameters, parameter)); + } else { + method = method.withParameters(ListUtils.insert(originalParameters, parameter, parameterIndex)); + } + + if (parameter.getTypeExpression() != null && !(parameter.getTypeExpression() instanceof J.Identifier || parameter.getTypeExpression() instanceof J.Primitive)) { + doAfterVisit(service(ImportService.class).shortenFullyQualifiedTypeReferencesIn(parameter.getTypeExpression())); + } + return method; + } + + private J.VariableDeclarations createParameter(J.MethodDeclaration method) { + TypeTree typeTree = createTypeTree(parameterType); + + return new J.VariableDeclarations( + randomId(), + Space.EMPTY, + Markers.EMPTY, + emptyList(), + emptyList(), + typeTree, + null, + emptyList(), + singletonList( + new JRightPadded<>( + new J.VariableDeclarations.NamedVariable( + randomId(), + Space.EMPTY, + Markers.EMPTY, + new J.Identifier( + randomId(), + Space.EMPTY, + Markers.EMPTY, + emptyList(), + parameterName, + typeTree.getType(), + new JavaType.Variable( + null, + 0, + parameterName, + method.getMethodType(), + typeTree.getType(), + null + ) + ), + emptyList(), + null, + null + ), + Space.EMPTY, + Markers.EMPTY + ) + ) + ); + } + + private TypeTree createTypeTree(String typeName) { + int arrayIndex = typeName.lastIndexOf('['); + if (arrayIndex != -1) { + TypeTree elementType = createTypeTree(typeName.substring(0, arrayIndex)); + return new J.ArrayType( + randomId(), + Space.EMPTY, + Markers.EMPTY, + elementType, + null, + JLeftPadded.build(Space.EMPTY), + new JavaType.Array(null, elementType.getType(), null) + ); + } + int genericsIndex = typeName.indexOf('<'); + if (genericsIndex != -1) { + TypeTree rawType = createTypeTree(typeName.substring(0, genericsIndex)); + List> typeParameters = new ArrayList<>(); + for (String typeParam : typeName.substring(genericsIndex + 1, typeName.lastIndexOf('>')).split(",")) { + typeParameters.add(JRightPadded.build((Expression) createTypeTree(typeParam.trim()))); + } + return new J.ParameterizedType( + randomId(), + Space.EMPTY, + Markers.EMPTY, + rawType, + JContainer.build(Space.EMPTY, typeParameters, Markers.EMPTY), + new JavaType.Parameterized(null, (JavaType.FullyQualified) rawType.getType(), null) + ); + } + JavaType.Primitive type = JavaType.Primitive.fromKeyword(typeName); + if (type != null) { + return new J.Primitive( + randomId(), + Space.EMPTY, + Markers.EMPTY, + type + ); + } + if (typeName.equals("?")) { + return new J.Wildcard( + randomId(), + Space.EMPTY, + Markers.EMPTY, + null, + null + ); + } + if (typeName.startsWith("?") && typeName.contains("extends")) { + return new J.Wildcard( + randomId(), + Space.EMPTY, + Markers.EMPTY, + new JLeftPadded<>(Space.SINGLE_SPACE, J.Wildcard.Bound.Extends, Markers.EMPTY), + createTypeTree(typeName.substring(typeName.indexOf("extends") + "extends".length() + 1).trim()).withPrefix(Space.SINGLE_SPACE) + ); + } + if (typeName.indexOf('.') == -1) { + String javaLangType = TypeUtils.findQualifiedJavaLangTypeName(typeName); + if (javaLangType != null) { + return new J.Identifier( + randomId(), + Space.EMPTY, + Markers.EMPTY, + emptyList(), + typeName, + JavaType.buildType(javaLangType), + null + ); + } + } + TypeTree typeTree = TypeTree.build(typeName); + // somehow the type attribution is incomplete, but `ChangeType` relies on this + if (typeTree instanceof J.FieldAccess) { + typeTree = ((J.FieldAccess) typeTree).withName(((J.FieldAccess) typeTree).getName().withType(typeTree.getType())); + } else if (typeTree.getType() == null) { + typeTree = ((J.Identifier) typeTree).withType(JavaType.ShallowClass.build(typeName)); + } + return typeTree; + } + } +} diff --git a/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java b/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java index b9ce823a9a9..d65f98c917a 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java @@ -446,43 +446,6 @@ public String toString() { } class TypeVisitor extends MethodSignatureParserBaseVisitor { - private static final Set COMMON_JAVA_LANG_TYPES = - new HashSet<>(Arrays.asList( - "Appendable", - "AutoCloseable", - "Boolean", - "Byte", - "Character", - "CharSequence", - "Class", - "ClassLoader", - "Cloneable", - "Comparable", - "Double", - "Enum", - "Error", - "Exception", - "Float", - "FunctionalInterface", - "Integer", - "Iterable", - "Long", - "Math", - "Number", - "Object", - "Readable", - "Record", - "Runnable", - "Short", - "String", - "StringBuffer", - "StringBuilder", - "System", - "Thread", - "Throwable", - "Void" - )); - @Override public String visitClassNameOrInterface(MethodSignatureParser.ClassNameOrInterfaceContext ctx) { StringBuilder classNameBuilder = new StringBuilder(); @@ -497,7 +460,7 @@ public String visitClassNameOrInterface(MethodSignatureParser.ClassNameOrInterfa if (Character.isLowerCase(beforeArr.charAt(0)) && JavaType.Primitive.fromKeyword(beforeArr) != null) { return className; } else { - if (COMMON_JAVA_LANG_TYPES.contains(beforeArr)) { + if (TypeUtils.findQualifiedJavaLangTypeName(beforeArr) != null) { return "java.lang." + className; } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java b/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java index d8907ed52c9..a765e31e9d8 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java @@ -17,11 +17,17 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.SourceFile; +import org.openrewrite.Tree; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.internal.DefaultJavaTypeSignatureBuilder; import org.openrewrite.java.service.ImportService; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaSourceFile; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.Space; import java.time.Duration; import java.util.HashMap; @@ -77,7 +83,6 @@ public static JavaVisitor modifyOnly(J2 subtree return getVisitor(subtree); } - @NonNull private static JavaVisitor getVisitor(@Nullable J scope) { return new JavaVisitor() { final Map usedTypes = new HashMap<>(); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/DeclaresType.java b/rewrite-java/src/main/java/org/openrewrite/java/search/DeclaresType.java index 7d5d96829fc..138ad9d633c 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/DeclaresType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/DeclaresType.java @@ -17,6 +17,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeUtils; import org.openrewrite.marker.SearchResult; public class DeclaresType

extends JavaIsoVisitor

{ @@ -28,10 +29,9 @@ public DeclaresType(String type) { @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P p) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, p); - if (cd.getType() != null && cd.getType().toString().equals(type)) { - return SearchResult.found(cd); + if (classDecl.getType() != null && TypeUtils.isOfClassType(classDecl.getType(), type)) { + return SearchResult.found(classDecl); } - return cd; + return super.visitClassDeclaration(classDecl, p); } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java b/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java index c895e63158c..117869d2f87 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java @@ -27,6 +27,42 @@ public class TypeUtils { private static final JavaType.Class TYPE_OBJECT = JavaType.ShallowClass.build("java.lang.Object"); + private static final Set COMMON_JAVA_LANG_TYPES = + new HashSet<>(Arrays.asList( + "Appendable", + "AutoCloseable", + "Boolean", + "Byte", + "Character", + "CharSequence", + "Class", + "ClassLoader", + "Cloneable", + "Comparable", + "Double", + "Enum", + "Error", + "Exception", + "Float", + "FunctionalInterface", + "Integer", + "Iterable", + "Long", + "Math", + "Number", + "Object", + "Readable", + "Record", + "Runnable", + "Short", + "String", + "StringBuffer", + "StringBuilder", + "System", + "Thread", + "Throwable", + "Void" + )); private TypeUtils() { } @@ -36,6 +72,10 @@ public static boolean isObject(@Nullable JavaType type) { "java.lang.Object".equals(((JavaType.FullyQualified) type).getFullyQualifiedName()); } + public static @Nullable String findQualifiedJavaLangTypeName(String name) { + return COMMON_JAVA_LANG_TYPES.contains(name) ? "java.lang." + name : null; + } + public static boolean isString(@Nullable JavaType type) { return type == JavaType.Primitive.String || (type instanceof JavaType.FullyQualified && diff --git a/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java b/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java new file mode 100644 index 00000000000..8bb5c672f2f --- /dev/null +++ b/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java @@ -0,0 +1,281 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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. + */ +package org.openrewrite.java; + +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class AddMethodParameterTest implements RewriteTest { + + @Test + void primitive() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "int", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + public class Foo { + public void bar(int i) { + } + } + """ + ) + ); + } + + @Test + void primitiveArray() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "int[]", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + public class Foo { + public void bar(int[] i) { + } + } + """ + ) + ); + } + + @Test + void parameterized() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "java.util.List", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + import java.util.List; + import java.util.regex.Pattern; + + public class Foo { + public void bar(List i) { + } + } + """ + ) + ); + } + + @Test + void wildcard() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "java.util.List", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + import java.util.List; + + public class Foo { + public void bar(List i) { + } + } + """ + ) + ); + } + + @Test + void wildcardExtends() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "java.util.List", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + import java.util.List; + + public class Foo { + public void bar(List i) { + } + } + """ + ) + ); + } + + @Test + void string() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "String", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + + public void bar(int i) { + } + + public void bar(int j) { + } + } + """, + """ + package foo; + + public class Foo { + public void bar(String i) { + } + + public void bar(int i) { + } + + public void bar(int j, String i) { + } + } + """ + ) + ); + } + + @Test + void first() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "int", "i", 0)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + + public void bar(int j) { + } + } + """, + """ + package foo; + + public class Foo { + public void bar(int i) { + } + + public void bar(int i, int j) { + } + } + """ + ) + ); + } + + @Test + void qualified() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "java.util.regex.Pattern", "p", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + public void bar(int j) { + } + } + """, + """ + package foo; + + import java.util.regex.Pattern; + + public class Foo { + public void bar(Pattern p) { + } + + public void bar(int j, Pattern p) { + } + } + """ + ) + ); + } + + @Test + void object() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("foo.Foo#bar(..)", "Object", "o", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + public class Foo { + public void bar(Object o) { + } + } + """ + ) + ); + } +} From d618b98e73c8867fb7136e27da5ae48f4a1add4b Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Mon, 2 Sep 2024 10:47:26 +0200 Subject: [PATCH 13/82] Add type pattern support to `AddMethodParameter` As `DeclaresType` doesn't support that, add custom precondition visitor. --- .../openrewrite/java/AddMethodParameter.java | 20 ++++++++++++++- .../java/AddMethodParameterTest.java | 25 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java b/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java index b180d810512..bfc881abc44 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java @@ -24,6 +24,7 @@ import org.openrewrite.java.service.ImportService; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; +import org.openrewrite.marker.SearchResult; import java.util.ArrayList; import java.util.List; @@ -81,7 +82,8 @@ public String getDescription() { public TreeVisitor getVisitor() { int idx = methodPattern.indexOf('#'); idx = idx == -1 ? methodPattern.indexOf(' ') : idx; - return Preconditions.check(new DeclaresType<>(methodPattern.substring(0, idx)), new AddNullMethodArgumentVisitor(methodPattern)); + boolean typePattern = idx != -1 && methodPattern.lastIndexOf('*', idx) != -1; + return Preconditions.check(typePattern ? new DeclaresMatchingType(methodPattern.substring(0, idx)) : new DeclaresType<>(methodPattern.substring(0, idx)), new AddNullMethodArgumentVisitor(methodPattern)); } private class AddNullMethodArgumentVisitor extends JavaIsoVisitor { @@ -257,4 +259,20 @@ private TypeTree createTypeTree(String typeName) { return typeTree; } } + + private static class DeclaresMatchingType extends JavaIsoVisitor { + private final TypeMatcher typeMatcher; + + public DeclaresMatchingType(String type) { + this.typeMatcher = new TypeMatcher(type); + } + + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + if (classDecl.getType() != null && typeMatcher.matches(classDecl.getType())) { + return SearchResult.found(classDecl); + } + return super.visitClassDeclaration(classDecl, ctx); + } + } } diff --git a/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java b/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java index 8bb5c672f2f..620e39c6b61 100644 --- a/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java +++ b/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java @@ -47,6 +47,31 @@ public void bar(int i) { ); } + @Test + void typePattern() { + rewriteRun( + spec -> spec.recipe(new AddMethodParameter("*..*#bar(..)", "int", "i", null)), + java( + """ + package foo; + + public class Foo { + public void bar() { + } + } + """, + """ + package foo; + + public class Foo { + public void bar(int i) { + } + } + """ + ) + ); + } + @Test void primitiveArray() { rewriteRun( From 062a919b0f626c6657c6c1e5f6323f6b7bf5b2c1 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Mon, 2 Sep 2024 11:17:58 +0200 Subject: [PATCH 14/82] newType may already be used (#4461) Co-authored-by: Laurens Westerlaken --- .../org/openrewrite/java/ChangeTypeTest.java | 48 +++++++++++++++++++ .../java/org/openrewrite/java/ChangeType.java | 1 + 2 files changed, 49 insertions(+) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java index f7deeb632b6..b2234b43a47 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/ChangeTypeTest.java @@ -1971,4 +1971,52 @@ void lombokMethod(@NonNull final String lombokNonNull){} ) ); } + + @Test + void shouldNotFullyQualifyWhenNewTypeIsAlreadyUsed() { + rewriteRun( + spec -> spec.recipe(new ChangeType( + "org.a.A", + "org.ab.AB", + true)), + java( + """ + package org.a; + + public class A { + public static String A = "A"; + } + """), + java( + """ + package org.ab; + + public class AB { + public static String A = "A"; + public static String B = "B"; + } + """), + // language=java + java( + """ + import org.a.A; + import org.ab.AB; + + class Letters { + String a = A.A; + String b = AB.B; + } + """, + """ + import org.ab.AB; + + class Letters { + String a = AB.A; + String b = AB.B; + } + """ + ) + ); + } + } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java index 79c1c1277ca..1d9504f3f10 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java @@ -518,6 +518,7 @@ private boolean hasNoConflictingImport(JavaSourceFile sf) { JavaType.FullyQualified currType = TypeUtils.asFullyQualified(anImport.getQualid().getType()); if (currType != null && !TypeUtils.isOfType(currType, oldType) && + !TypeUtils.isOfType(currType, newType) && currType.getClassName().equals(newType.getClassName())) { return false; } From 03cd38d35faaa97332d3cc4dfbc793b44bdc6510 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Mon, 2 Sep 2024 11:39:54 +0200 Subject: [PATCH 15/82] `AddMethodParameter`: Only format what is necessary --- .../main/java/org/openrewrite/java/AddMethodParameter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java b/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java index bfc881abc44..f2746c75c68 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/AddMethodParameter.java @@ -104,9 +104,9 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } } J.VariableDeclarations parameter = createParameter(method); - method = addParameter(method, parameter); + method = autoFormat(addParameter(method, parameter), parameter, ctx, getCursor().getParentTreeCursor()); } - return autoFormat(method, ctx); + return method; } private J.MethodDeclaration addParameter(J.MethodDeclaration method, J.VariableDeclarations parameter) { From 2b877c61ccca7f287fb79de97725e4276d10631e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Mon, 2 Sep 2024 18:14:42 -0400 Subject: [PATCH 16/82] Revert "Force lowercase urls and origins in `GitRemote` (#4456)" (#4462) This reverts commit 758bbd8201c37fda503632fcac2f6547e7169e41. --- .../src/main/java/org/openrewrite/GitRemote.java | 13 ++++--------- .../test/java/org/openrewrite/GitRemoteTest.java | 15 ++++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java index fa9f6b06d41..dd85812bd7a 100644 --- a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java +++ b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java @@ -231,8 +231,7 @@ private String repositoryPath(RemoteServerMatch match, URI normalizedUri) { private static final Pattern PORT_PATTERN = Pattern.compile(":\\d+"); - static URI normalize(String original) { - String url = original.toLowerCase(Locale.ENGLISH); + static URI normalize(String url) { try { URIish uri = new URIish(url); String scheme = uri.getScheme(); @@ -267,7 +266,7 @@ static URI normalize(String original) { String path = uri.getPath().replaceFirst("/$", "") .replaceFirst("\\.git$", "") .replaceFirst("^/", ""); - return URI.create((scheme + "://" + host + maybePort + "/" + path).replaceFirst("/$", "").toLowerCase(Locale.ENGLISH)); + return URI.create((scheme + "://" + host + maybePort + "/" + path).replaceFirst("/$", "")); } catch (URISyntaxException e) { throw new IllegalStateException("Unable to parse origin from: " + url, e); } @@ -306,12 +305,8 @@ public RemoteServer(Service service, String origin, URI... uris) { public RemoteServer(Service service, String origin, Collection uris) { this.service = service; - this.origin = origin.toLowerCase(Locale.ENGLISH); - this.uris.addAll(uris.stream() - .map(URI::toString) - .map(urlString -> urlString.toLowerCase(Locale.ENGLISH)) - .map(URI::create) - .collect(Collectors.toList())); + this.origin = origin; + this.uris.addAll(uris); } private GitRemote.Parser.@Nullable RemoteServerMatch match(URI normalizedUri) { diff --git a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java index 013cd8d965a..2fb0991b2b1 100644 --- a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java @@ -39,16 +39,13 @@ public class GitRemoteTest { git@gitlab.com:group/subgroup/subergroup/subestgroup/repo.git, gitlab.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo ssh://git@gitlab.com:22/group/subgroup/subergroup/subestgroup/repo.git, gitlab.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo - https://bitbucket.org/PRJ/repo, bitbucket.org, prj/repo, prj, repo - git@bitbucket.org:PRJ/repo.git, bitbucket.org, prj/repo, prj, repo - ssh://bitbucket.org/PRJ/repo.git, bitbucket.org, prj/repo, prj, repo + https://bitbucket.org/PRJ/repo, bitbucket.org, PRJ/repo, PRJ, repo + git@bitbucket.org:PRJ/repo.git, bitbucket.org, PRJ/repo, PRJ, repo + ssh://bitbucket.org/PRJ/repo.git, bitbucket.org, PRJ/repo, PRJ, repo https://org@dev.azure.com/org/project/_git/repo, dev.azure.com, org/project/repo, org/project, repo https://dev.azure.com/org/project/_git/repo, dev.azure.com, org/project/repo, org/project, repo git@ssh.dev.azure.com:v3/org/project/repo, dev.azure.com, org/project/repo, org/project, repo - - HTTPS://GITHUB.COM/ORG/REPO.GIT, github.com, org/repo, org, repo - HtTpS://GitHub.CoM/OrG/R3P0.GIT, github.com, org/r3p0, org, r3p0 """) void parseKnownRemotes(String cloneUrl, String expectedOrigin, String expectedPath, String expectedOrganization, String expectedRepositoryName) { GitRemote.Parser parser = new GitRemote.Parser(); @@ -86,9 +83,9 @@ void parseUnknownRemote(String cloneUrl, String expectedOrigin, String expectedP https://scm.company.com:443/stash/scm/org/repo.git, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo https://scm.company.com:1234/stash/scm/org/repo.git, https://scm.company.com:1234/stash, Bitbucket, scm.company.com:1234/stash, org/repo, org, repo git@scm.company.com:stash/org/repo.git, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo - ssh://scm.CompanY.com/stash/org/repo, scm.cOMpAnY.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo + ssh://scm.company.com/stash/org/repo, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo ssh://scm.company.com:22/stash/org/repo, scm.company.com/stash, Bitbucket, scm.company.com/stash, org/repo, org, repo - ssh://scm.company.com:7999/stash/org/repo, ssh://sCm.company.com:7999/stash, Bitbucket, scm.company.com:7999/stash, org/repo, org, repo + ssh://scm.company.com:7999/stash/org/repo, ssh://scm.company.com:7999/stash, Bitbucket, scm.company.com:7999/stash, org/repo, org, repo https://scm.company.com/very/long/context/path/org/repo.git, scm.company.com/very/long/context/path, Bitbucket, scm.company.com/very/long/context/path, org/repo, org, repo https://scm.company.com:1234/very/long/context/path/org/repo.git, https://scm.company.com:1234/very/long/context/path, Bitbucket, scm.company.com:1234/very/long/context/path, org/repo, org, repo @@ -100,7 +97,7 @@ void parseUnknownRemote(String cloneUrl, String expectedOrigin, String expectedP git@scm.company.com:group/subgroup/subergroup/subestgroup/repo.git, ssh://scm.company.com, GitLab, scm.company.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo https://scm.company.com:443/group/subgroup/subergroup/subestgroup/repo.git, scm.company.com, GitLab, scm.company.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo ssh://scm.company.com:22/group/subgroup/subergroup/subestgroup/repo.git, ssh://scm.company.com, GitLab, scm.company.com, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo - ssh://SCM.COMPANY.COM:222/group/subgroup/subergroup/subestgroup/repo.git, ssh://SCM.CoMpAnY.cOm:222, GitLab, scm.company.com:222, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo + ssh://scm.company.com:222/group/subgroup/subergroup/subestgroup/repo.git, ssh://scm.company.com:222, GitLab, scm.company.com:222, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo https://scm.company.com/very/long/context/path/group/subgroup/subergroup/subestgroup/repo, scm.company.com/very/long/context/path, GitLab, scm.company.com/very/long/context/path, group/subgroup/subergroup/subestgroup/repo, group/subgroup/subergroup/subestgroup, repo """) From 458a35a8e95d9be0466b8d35ce83c06e018adaad Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 3 Sep 2024 07:32:56 +0000 Subject: [PATCH 17/82] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../test/java/org/openrewrite/java/AddMethodParameterTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java b/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java index 620e39c6b61..4c39321bfe3 100644 --- a/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java +++ b/rewrite-java/src/test/java/org/openrewrite/java/AddMethodParameterTest.java @@ -16,12 +16,14 @@ package org.openrewrite.java; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; class AddMethodParameterTest implements RewriteTest { + @DocumentExample @Test void primitive() { rewriteRun( From 1f4564cbdb0e7ac20fc364b1daa7689a78b709ef Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Tue, 3 Sep 2024 11:28:36 +0200 Subject: [PATCH 18/82] Retain default behaviour when we cannot determine conflict (#4464) Co-authored-by: Laurens Westerlaken --- .../src/main/java/org/openrewrite/java/ChangeType.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java index 1d9504f3f10..2ae65f707ff 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java @@ -307,7 +307,7 @@ public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) { .withType(null) .withPrefix(ident.getPrefix())); } else { - if (sf != null && hasNoConflictingImport(sf)) { + if (hasNoConflictingImport(sf)) { ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getClassName()); } else { ident = ident.withSimpleName(((JavaType.FullyQualified) targetType).getFullyQualifiedName()); @@ -508,11 +508,11 @@ private boolean isTargetFullyQualifiedType(JavaType.@Nullable FullyQualified fq) return fq != null && TypeUtils.isOfClassType(fq, originalType.getFullyQualifiedName()) && targetType instanceof JavaType.FullyQualified; } - private boolean hasNoConflictingImport(JavaSourceFile sf) { + private boolean hasNoConflictingImport(@Nullable JavaSourceFile sf) { JavaType.FullyQualified oldType = TypeUtils.asFullyQualified(originalType); JavaType.FullyQualified newType = TypeUtils.asFullyQualified(targetType); - if (oldType == null || newType == null) { - return false; // No way to be sure + if (sf == null || oldType == null || newType == null) { + return true; // No way to be sure so we retain previous behaviour } for (J.Import anImport : sf.getImports()) { JavaType.FullyQualified currType = TypeUtils.asFullyQualified(anImport.getQualid().getType()); From 25f442fc7be391e4b3b32938f5f1c5d477dd4de6 Mon Sep 17 00:00:00 2001 From: Peter Streef Date: Tue, 3 Sep 2024 17:24:43 +0200 Subject: [PATCH 19/82] Case insensitive origin matching (#4465) --- .../main/java/org/openrewrite/GitRemote.java | 27 ++++++++++++------- .../java/org/openrewrite/GitRemoteTest.java | 17 ++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java index dd85812bd7a..22bc3c63743 100644 --- a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java +++ b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java @@ -80,7 +80,10 @@ public URI toUri(GitRemote remote, String protocol) { selectedBaseUrl = URI.create(protocol + "://" + stripProtocol(remote.origin)); } else { selectedBaseUrl = servers.stream() - .filter(server -> server.allOrigins().contains(stripProtocol(remote.origin))) + .filter(server -> server.allOrigins() + .stream() + .anyMatch(origin -> origin.equalsIgnoreCase(stripProtocol(remote.origin))) + ) .flatMap(server -> server.getUris().stream()) .filter(uri -> uri.getScheme().equals(protocol)) .findFirst() @@ -156,7 +159,7 @@ public Parser registerRemote(Service service, String origin) { } public RemoteServer findRemoteServer(String origin) { - return servers.stream().filter(server -> server.origin.equals(origin)) + return servers.stream().filter(server -> server.origin.equalsIgnoreCase(origin)) .findFirst() .orElseGet(() -> { URI normalizedUri = normalize(origin); @@ -166,7 +169,7 @@ public RemoteServer findRemoteServer(String origin) { } private void add(RemoteServer server) { - if (server.service != Service.Unknown || servers.stream().noneMatch(s -> s.origin.equals(server.origin))) { + if (server.service != Service.Unknown || servers.stream().noneMatch(s -> s.origin.equalsIgnoreCase(server.origin))) { servers.add(server); } } @@ -179,15 +182,15 @@ public GitRemote parse(String url) { switch (match.service) { case AzureDevOps: - if (match.matchedUri.getHost().equals("ssh.dev.azure.com")) { - repositoryPath = repositoryPath.replaceFirst("v3/", ""); + if (match.matchedUri.getHost().equalsIgnoreCase("ssh.dev.azure.com")) { + repositoryPath = repositoryPath.replaceFirst("(?i)v3/", ""); } else { - repositoryPath = repositoryPath.replaceFirst("/_git/", "/"); + repositoryPath = repositoryPath.replaceFirst("(?i)/_git/", "/"); } break; case Bitbucket: if (url.startsWith("http")) { - repositoryPath = repositoryPath.replaceFirst("scm/", ""); + repositoryPath = repositoryPath.replaceFirst("(?i)scm/", ""); } break; } @@ -222,7 +225,9 @@ private String repositoryPath(RemoteServerMatch match, URI normalizedUri) { String uri = normalizedUri.toString(); String contextPath = origin.getPath(); String path = normalizedUri.getPath(); - if (!normalizedUri.getHost().equals(origin.getHost()) || normalizedUri.getPort() != origin.getPort() || !path.startsWith(contextPath)) { + if (!normalizedUri.getHost().equalsIgnoreCase(origin.getHost()) || + normalizedUri.getPort() != origin.getPort() || + !path.toLowerCase(Locale.ENGLISH).startsWith(contextPath.toLowerCase(Locale.ENGLISH))) { throw new IllegalArgumentException("Origin: " + origin + " does not match the clone url: " + uri); } return path.substring(contextPath.length()) @@ -264,7 +269,7 @@ static URI normalize(String url) { String maybePort = maybePort(uri.getPort(), scheme); String path = uri.getPath().replaceFirst("/$", "") - .replaceFirst("\\.git$", "") + .replaceFirst("(?i)\\.git$", "") .replaceFirst("^/", ""); return URI.create((scheme + "://" + host + maybePort + "/" + path).replaceFirst("/$", "")); } catch (URISyntaxException e) { @@ -310,8 +315,10 @@ public RemoteServer(Service service, String origin, Collection uris) { } private GitRemote.Parser.@Nullable RemoteServerMatch match(URI normalizedUri) { + String lowerCaseNormalizedUri = normalizedUri.toString().toLowerCase(Locale.ENGLISH); for (URI uri : uris) { - if (normalizedUri.toString().startsWith(Parser.normalize(uri.toString()).toString())) { + String normalizedServerUri = Parser.normalize(uri.toString()).toString().toLowerCase(Locale.ENGLISH); + if (lowerCaseNormalizedUri.startsWith(normalizedServerUri)) { return new Parser.RemoteServerMatch(service, origin, uri); } } diff --git a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java index 2fb0991b2b1..8247c3d89a9 100644 --- a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java @@ -240,4 +240,21 @@ void findRemote() { assertThat(parser.findRemoteServer("scm.unregistered.com").getOrigin()).isEqualTo("scm.unregistered.com"); assertThat(parser.findRemoteServer("https://scm.unregistered.com").getOrigin()).isEqualTo("scm.unregistered.com"); } + + @ParameterizedTest + @CsvSource(textBlock = """ + https://github.com/org/repo, github.com, org/repo, org, repo + https://GITHUB.COM/ORG/REPO, github.com, ORG/REPO, ORG, REPO + ssh://GITHUB.COM/ORG/REPO.GIT, github.com, ORG/REPO, ORG, REPO + https://DEV.AZURE.COM/ORG/PROJECT/_GIT/REPO, dev.azure.com, ORG/PROJECT/REPO, ORG/PROJECT, REPO + GIT@SSH.DEV.AZURE.COM:V3/ORG/PROJECT/REPO, dev.azure.com, ORG/PROJECT/REPO, ORG/PROJECT, REPO + """) + void parseOriginCaseInsensitive(String cloneUrl, String expectedOrigin, String expectedPath, String expectedOrganization, String expectedRepositoryName) { + GitRemote.Parser parser = new GitRemote.Parser(); + GitRemote remote = parser.parse(cloneUrl); + assertThat(remote.getOrigin()).isEqualTo(expectedOrigin); + assertThat(remote.getPath()).isEqualTo(expectedPath); + assertThat(remote.getOrganization()).isEqualTo(expectedOrganization); + assertThat(remote.getRepositoryName()).isEqualTo(expectedRepositoryName); + } } From 1cf7a3a21218b4c80f48470cd821a9018f065c9f Mon Sep 17 00:00:00 2001 From: Peter Streef Date: Wed, 4 Sep 2024 16:58:02 +0200 Subject: [PATCH 20/82] Add static constructor for `GitRemote.Service` from any previously used name formats (#4469) --- .../main/java/org/openrewrite/GitRemote.java | 19 ++++++++++++++++++- .../java/org/openrewrite/GitRemoteTest.java | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java index 22bc3c63743..1da3335207d 100644 --- a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java +++ b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java @@ -44,7 +44,24 @@ public enum Service { Bitbucket, BitbucketCloud, AzureDevOps, - Unknown + Unknown; + + public static Service forName(String serviceName) { + switch (serviceName.toLowerCase(Locale.ENGLISH).replaceAll("[-_ ]", "")) { + case "github": + return GitHub; + case "gitlab": + return GitLab; + case "bitbucket": + return Bitbucket; + case "bitbucketcloud": + return BitbucketCloud; + case "azuredevops": + return AzureDevOps; + default: + return Unknown; + } + } } public static class Parser { diff --git a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java index 8247c3d89a9..c6d86ea15af 100644 --- a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java @@ -257,4 +257,21 @@ void parseOriginCaseInsensitive(String cloneUrl, String expectedOrigin, String e assertThat(remote.getOrganization()).isEqualTo(expectedOrganization); assertThat(remote.getRepositoryName()).isEqualTo(expectedRepositoryName); } + + @ParameterizedTest + @CsvSource(textBlock = """ + GitHub, GitHub + GITLAB, GitLab + bitbucket, Bitbucket + BitbucketCloud, BitbucketCloud + Bitbucket Cloud, BitbucketCloud + BITBUCKET_CLOUD, BitbucketCloud + AzureDevOps, AzureDevOps + AZURE_DEVOPS, AzureDevOps + Azure DevOps, AzureDevOps + idontknow, Unknown + """) + void findServiceForName(String name, GitRemote.Service service){ + assertThat(GitRemote.Service.forName(name)).isEqualTo(service); + } } From bc70b5983a32c56b408dbf2e0fc482b2e0158c20 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 4 Sep 2024 21:04:59 +0200 Subject: [PATCH 21/82] Add custom error listener to JSON and XML lexers --- .../src/main/java/org/openrewrite/json/JsonParser.java | 6 ++++-- .../src/main/java/org/openrewrite/xml/XmlParser.java | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java b/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java index 80708eb9503..e3494066a2e 100755 --- a/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java +++ b/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java @@ -39,9 +39,11 @@ public Stream parseInputs(Iterable sourceFiles, @Nullable Pat return acceptedInputs(sourceFiles).map(input -> { parsingListener.startedParsing(input); try (InputStream sourceStream = input.getSource(ctx)) { - JSON5Parser parser = new JSON5Parser(new CommonTokenStream(new JSON5Lexer( - CharStreams.fromStream(sourceStream)))); + JSON5Lexer lexer = new JSON5Lexer(CharStreams.fromStream(sourceStream)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ForwardingErrorListener(input.getPath(), ctx)); + JSON5Parser parser = new JSON5Parser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); parser.addErrorListener(new ForwardingErrorListener(input.getPath(), ctx)); diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java index fd107e5c7f9..e9e323443a8 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java @@ -42,9 +42,11 @@ public Stream parseInputs(Iterable sourceFiles, @Nullable Pat try (EncodingDetectingInputStream is = input.getSource(ctx)) { String sourceStr = is.readFully(); - XMLParser parser = new XMLParser(new CommonTokenStream(new XMLLexer( - CharStreams.fromString(sourceStr)))); + XMLLexer lexer = new XMLLexer(CharStreams.fromString(sourceStr)); + lexer.removeErrorListeners(); + lexer.addErrorListener(new ForwardingErrorListener(input.getPath(), ctx)); + XMLParser parser = new XMLParser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); parser.addErrorListener(new ForwardingErrorListener(input.getPath(), ctx)); From 153700c11fda7a51b0597e5f14876c6c98fcb2d9 Mon Sep 17 00:00:00 2001 From: Peter Streef Date: Wed, 4 Sep 2024 21:08:46 +0200 Subject: [PATCH 22/82] Ignore case on GitRemote equals (#4468) * Ignore case on GitRemote equals * Add tests --- .../main/java/org/openrewrite/GitRemote.java | 28 +++++++++++++++++++ .../java/org/openrewrite/GitRemoteTest.java | 12 ++++++++ 2 files changed, 40 insertions(+) diff --git a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java index 1da3335207d..b56b0997ed2 100644 --- a/rewrite-core/src/main/java/org/openrewrite/GitRemote.java +++ b/rewrite-core/src/main/java/org/openrewrite/GitRemote.java @@ -16,6 +16,7 @@ package org.openrewrite; import lombok.Value; +import org.apache.commons.lang3.StringUtils; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.openrewrite.jgit.transport.URIish; @@ -38,6 +39,33 @@ public class GitRemote { String repositoryName; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GitRemote gitRemote = (GitRemote) o; + return service == gitRemote.service && + StringUtils.equalsIgnoreCase(url, gitRemote.url) && + StringUtils.equalsIgnoreCase(origin, gitRemote.origin) && + StringUtils.equalsIgnoreCase(path, gitRemote.path) && + StringUtils.equalsIgnoreCase(organization, gitRemote.organization) && + StringUtils.equalsIgnoreCase(repositoryName, gitRemote.repositoryName); + } + + @Override + public int hashCode() { + return Objects.hash(service, + url == null ? null : url.toLowerCase(Locale.ENGLISH), + origin == null ? null : origin.toLowerCase(Locale.ENGLISH), + path == null ? null : path.toLowerCase(Locale.ENGLISH), + organization == null ? null : organization.toLowerCase(Locale.ENGLISH), + repositoryName == null ? null : repositoryName.toLowerCase(Locale.ENGLISH)); + } + public enum Service { GitHub, GitLab, diff --git a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java index c6d86ea15af..d765cd8aa1d 100644 --- a/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/GitRemoteTest.java @@ -274,4 +274,16 @@ void parseOriginCaseInsensitive(String cloneUrl, String expectedOrigin, String e void findServiceForName(String name, GitRemote.Service service){ assertThat(GitRemote.Service.forName(name)).isEqualTo(service); } + + @Test + void equalsIgnoresCase() { + assertThat(new GitRemote(GitRemote.Service.GitHub, "https://github.com/org/repo", "github.com", "org/repo", "org", "repo")) + .isEqualTo(new GitRemote(GitRemote.Service.GitHub, "https://GITHUB.COM/ORG/REPO", "GITHUB.COM", "ORG/REPO", "ORG", "REPO")); + } + + @Test + void hashCodeIgnoresCase() { + assertThat(new GitRemote(GitRemote.Service.GitHub, "https://github.com/org/repo", "github.com", "org/repo", "org", "repo")) + .hasSameHashCodeAs(new GitRemote(GitRemote.Service.GitHub, "https://GITHUB.COM/ORG/REPO", "GITHUB.COM", "ORG/REPO", "ORG", "REPO")); + } } From ac74ea6a076c917c9b4cdfe74fc07b232501cefc Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Wed, 4 Sep 2024 22:57:00 +0200 Subject: [PATCH 23/82] Add DoesNotUseType recipe for use in preconditions to reject source files (#4471) * Initial setup with test cases * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * PR feedback * Remove unused should be true so it shows the recipe did not run * Apply formatter to NotUsesTypeTest * PR feedback * Add missing options and actually run recipe in test --------- Co-authored-by: Laurens Westerlaken Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../java/search/DoesNotUseType.java | 53 ++++++++++++ .../java/search/DoesNotUseTypeTest.java | 80 +++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 rewrite-java/src/main/java/org/openrewrite/java/search/DoesNotUseType.java create mode 100644 rewrite-java/src/test/java/org/openrewrite/java/search/DoesNotUseTypeTest.java diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/DoesNotUseType.java b/rewrite-java/src/main/java/org/openrewrite/java/search/DoesNotUseType.java new file mode 100644 index 00000000000..5de18770385 --- /dev/null +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/DoesNotUseType.java @@ -0,0 +1,53 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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. + */ +package org.openrewrite.java.search; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.*; + +@Value +@EqualsAndHashCode(callSuper = false) +public class DoesNotUseType extends Recipe { + + @Option(displayName = "Fully-qualified type name", + description = "A fully-qualified type name, that is used to find matching type references. " + + "Supports glob expressions. `java..*` finds every type from every subpackage of the `java` package.", + example = "java.util.List") + String fullyQualifiedTypeName; + + @Option(displayName = "Include implicit type references", + description = "Whether to include implicit type references, such as those in method signatures.", + required = false) + @Nullable + Boolean includeImplicit; + + @Override + public String getDisplayName() { + return "Check whether a type is **not** in use"; + } + + @Override + public String getDescription() { + return "Useful as a precondition to skip over compilation units using the argument type."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.not(new UsesType<>(fullyQualifiedTypeName, includeImplicit)); + } +} diff --git a/rewrite-java/src/test/java/org/openrewrite/java/search/DoesNotUseTypeTest.java b/rewrite-java/src/test/java/org/openrewrite/java/search/DoesNotUseTypeTest.java new file mode 100644 index 00000000000..4cdc9977b73 --- /dev/null +++ b/rewrite-java/src/test/java/org/openrewrite/java/search/DoesNotUseTypeTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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. + */ +package org.openrewrite.java.search; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class DoesNotUseTypeTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromYaml( + """ + type: specs.openrewrite.org/v1beta/recipe + name: org.acme.RemoveUnusedImportsIfNotUsingString + description: Test. + preconditions: + - org.openrewrite.java.search.DoesNotUseType: + fullyQualifiedTypeName: java.lang.String + includeImplicit: true + recipeList: + - org.openrewrite.java.RemoveUnusedImports + """, + "org.acme.RemoveUnusedImportsIfNotUsingString" + ); + } + + @DocumentExample + @Test + void importRemovedWhenTypeNotFound() { + rewriteRun( + java( + """ + import java.lang.StringBuilder; + + class Foo { + int bla = 123; + } + """, + """ + class Foo { + int bla = 123; + } + """ + ) + ); + } + + @Test + void importRetainedWhenTypeInUse() { + rewriteRun( + java( + """ + import java.lang.StringBuilder; + + class Foo { + String bla = "bla"; + } + """ + ) + ); + } +} From 350454123fc8818208aac5afb665c50e9a95a991 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Wed, 4 Sep 2024 16:31:21 -0500 Subject: [PATCH 24/82] Add missing buildscript classpath configuration (#4459) * Add missing buildscript classpath configuration * Rollback updates to recipes to ease release --- rewrite-gradle/build.gradle.kts | 3 +- .../gradle/marker/GradleBuildscript.java | 43 +++++++++++++++++++ .../gradle/marker/GradleProject.java | 21 ++++++++- .../gradle/marker/GradleSettings.java | 40 ++++++++++++++--- 4 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java diff --git a/rewrite-gradle/build.gradle.kts b/rewrite-gradle/build.gradle.kts index 109bf35375c..6a13b318e64 100644 --- a/rewrite-gradle/build.gradle.kts +++ b/rewrite-gradle/build.gradle.kts @@ -89,6 +89,7 @@ tasks.named("processResources") { //Javadoc compiler will complain about the use of the internal types. tasks.withType { exclude( - "**/GradleProject**" + "**/GradleProject**", + "**/GradleSettings**" ) } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java new file mode 100644 index 00000000000..7c08a94dfba --- /dev/null +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java @@ -0,0 +1,43 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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. + */ +package org.openrewrite.gradle.marker; + +import lombok.Value; +import lombok.With; +import org.jspecify.annotations.Nullable; +import org.openrewrite.maven.tree.MavenRepository; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Value +@With +public class GradleBuildscript implements Serializable { + UUID id; + List mavenRepositories; + Map nameToConfiguration; + + public @Nullable GradleDependencyConfiguration getConfiguration(String name) { + return nameToConfiguration.get(name); + } + + public List getConfigurations() { + return new ArrayList<>(nameToConfiguration.values()); + } +} diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java index 3e77f0bdd4f..3aa601e2571 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java @@ -60,24 +60,40 @@ public class GradleProject implements Marker, Serializable { List mavenRepositories; @With + @Deprecated + @Nullable List mavenPluginRepositories; Map nameToConfiguration; + GradleBuildscript buildscript; + // Backwards compatibility to ease convoluted release process with rewrite-gradle-tooling-model public GradleProject( UUID id, + String group, String name, + String version, String path, List plugins, List mavenRepositories, List mavenPluginRepositories, Map nameToConfiguration ) { - this(id, "", name, "", path, plugins, mavenRepositories, mavenPluginRepositories, nameToConfiguration); + this(id, group, name, version, path, plugins, mavenRepositories, mavenPluginRepositories, nameToConfiguration, null); } + /** + * Get a list of Maven plugin repositories. + * + * @return list of Maven plugin repositories + * @deprecated Use {@link GradleBuildscript#getMavenRepositories()} instead. + */ + @Deprecated public List getMavenPluginRepositories() { + if (buildscript != null) { + return buildscript.getMavenRepositories(); + } return mavenPluginRepositories == null ? Collections.emptyList() : mavenPluginRepositories; } @@ -151,7 +167,8 @@ public GradleProject withNameToConfiguration(Map pluginRepositories; + List plugins; Map featurePreviews; + GradleBuildscript buildscript; + + // Backwards compatibility to ease convoluted release process with rewrite-gradle-tooling-model + public GradleSettings( + UUID id, + List pluginRepositories, + List plugins, + Map featurePreviews + ) { + this(id, pluginRepositories, plugins, featurePreviews, null); + } public @Nullable Boolean isFeatureEnabled(String name) { - // Unclear how enabled status can be determined in latest gradle APIs - return null; + return featurePreviews.get(name).getEnabled(); } public Set getActiveFeatures() { @@ -46,4 +60,18 @@ public Set getActiveFeatures() { .filter(FeaturePreview::isActive) .collect(Collectors.toSet()); } + + /** + * Get a list of Maven plugin repositories. + * + * @return list of Maven plugin repositories + * @deprecated Use {@link GradleBuildscript#getMavenRepositories()} instead. + */ + @Deprecated + public List getPluginRepositories() { + if (buildscript != null) { + return buildscript.getMavenRepositories(); + } + return pluginRepositories == null ? Collections.emptyList() : pluginRepositories; + } } From ca78f0c0a43464a945b542c14b921fc2f6b6b6f1 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Thu, 5 Sep 2024 13:22:13 -0500 Subject: [PATCH 25/82] Remove deprecated methods now that a new version of the tooling api is published --- .../org/openrewrite/gradle/DependencyVersionSelector.java | 4 ++-- .../java/org/openrewrite/gradle/plugins/ChangePlugin.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java index 90a6019344d..e2c549521d2 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java @@ -167,11 +167,11 @@ private MavenMetadata downloadMetadata(String groupId, String artifactId, List determineRepos(@Nullable String configuration) { if (gradleSettings != null) { - return gradleSettings.getPluginRepositories(); + return gradleSettings.getBuildscript().getMavenRepositories(); } Objects.requireNonNull(gradleProject); return "classpath".equals(configuration) ? - gradleProject.getMavenPluginRepositories() : + gradleProject.getBuildscript().getMavenRepositories() : gradleProject.getMavenRepositories(); } } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java index 4bc90b42be2..252ac358736 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java @@ -247,9 +247,9 @@ private J.MethodInvocation maybeUpdateApplySyntax(J.MethodInvocation m) { private List getPluginRepositories() { if (gradleProject != null) { - return gradleProject.getMavenPluginRepositories(); + return gradleProject.getBuildscript().getMavenRepositories(); } - return gradleSettings.getPluginRepositories(); + return gradleSettings.getBuildscript().getMavenRepositories(); } } ); From c33900717d0603f1c46eec2528634a4acef5071c Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Fri, 6 Sep 2024 19:06:11 +0200 Subject: [PATCH 26/82] Static import is removed and leaves ambiguous reference behind (#4450) * Test case that shows static import is removed leaving ambiguous reference behind * Prevent imports of static members from being removed when this causes ambiguous references * First small fix * Destream and invert * Fixed final test * Revert change to TypeInUse and detect from variables only --------- Co-authored-by: Laurens Westerlaken Co-authored-by: Tim te Beek --- .../java/RemoveUnusedImportsTest.java | 100 +++++++++++++++++- .../openrewrite/java/RemoveUnusedImports.java | 38 ++++++- 2 files changed, 129 insertions(+), 9 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java index a23297e0cb4..70637a63134 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java @@ -22,6 +22,7 @@ import org.openrewrite.style.NamedStyles; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; import org.openrewrite.test.TypeValidation; import static java.util.Collections.emptySet; @@ -1380,22 +1381,24 @@ void removeMultipleWildcardImports() { import java.util.*; import static java.util.Collections.emptyList; + import static java.util.Collections.singletonList; class A { Collection c = emptyList(); Set s = new HashSet<>(); - List l = singletonList("a","b","c"); + List l = singletonList("a"); } """, """ import java.util.*; import static java.util.Collections.emptyList; + import static java.util.Collections.singletonList; class A { Collection c = emptyList(); Set s = new HashSet<>(); - List l = singletonList("a","b","c"); + List l = singletonList("a"); } """ ) @@ -1449,7 +1452,7 @@ void removeDirectImportWithWildcardImport() { class A { Collection c = emptyList(); Set s = emptySet(); - List l = singletonList("c","b","a"); + List l = singletonList("c"); Iterator i = emptyIterator(); } """, @@ -1461,7 +1464,7 @@ class A { class A { Collection c = emptyList(); Set s = emptySet(); - List l = singletonList("c","b","a"); + List l = singletonList("c"); Iterator i = emptyIterator(); } """ @@ -1814,4 +1817,93 @@ public abstract class Foo2 { ) ); } + + @Test + void staticAmbiguousImport() { + // language=java + rewriteRun( + java( + """ + package org.a; + public class Abc { + public static String A = "A"; + public static String B = "B"; + public static String C = "C"; + public static String ALL = "%s%s%s".formatted(A, B, C); + } + """, + SourceSpec::skip + ), + java( + """ + package org.b; + public class Def { + public static String D = "D"; + public static String E = "E"; + public static String F = "F"; + public static String ALL = "%s%s%s".formatted(D, E, F); + } + """, + SourceSpec::skip + ), + // No change when removal would cause ambiguity + java( + """ + package org.test; + + import static org.a.Abc.*; + import static org.a.Abc.ALL; + import static org.b.Def.*; + + public class Foo { + private String abc = ALL; + private String a = A; + private String b = B; + private String c = C; + private String d = D; + private String e = E; + private String f = F; + } + """ + ), + // Do still remove unambiguous imports + java( + """ + package org.test; + + import static org.a.Abc.*; + import static org.a.Abc.A; + import static org.a.Abc.ALL; + import static org.b.Def.*; + + public class Bar { + private String abc = ALL; + private String a = A; + private String b = B; + private String c = C; + private String d = D; + private String e = E; + private String f = F; + } + """, + """ + package org.test; + + import static org.a.Abc.*; + import static org.a.Abc.ALL; + import static org.b.Def.*; + + public class Bar { + private String abc = ALL; + private String a = A; + private String b = B; + private String c = C; + private String d = D; + private String e = E; + private String f = F; + } + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java b/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java index 2055b2bc251..856ca707f2f 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java @@ -169,7 +169,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon anImport.used = true; usedStaticWildcardImports.add(elem.getTypeName()); } else if (((methodsAndFields == null ? 0 : methodsAndFields.size()) + - (staticClasses == null ? 0 : staticClasses.size())) < layoutStyle.getNameCountToUseStarImport()) { + (staticClasses == null ? 0 : staticClasses.size())) < layoutStyle.getNameCountToUseStarImport()) { // replacing the star with a series of unfolded imports anImport.imports.clear(); @@ -199,8 +199,8 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon usedStaticWildcardImports.add(elem.getTypeName()); } } else if (staticClasses != null && staticClasses.stream().anyMatch(c -> elem.getTypeName().equals(c.getFullyQualifiedName())) || - (methodsAndFields != null && methodsAndFields.contains(qualid.getSimpleName())) || - (targetMethodsAndFields != null && targetMethodsAndFields.contains(qualid.getSimpleName()))) { + (methodsAndFields != null && methodsAndFields.contains(qualid.getSimpleName())) || + (targetMethodsAndFields != null && targetMethodsAndFields.contains(qualid.getSimpleName()))) { anImport.used = true; } else { anImport.used = false; @@ -252,11 +252,13 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon } // Do not use direct imports that are imported by a wildcard import + Set ambiguousStaticImportNames = getAmbiguousStaticImportNames(cu); for (ImportUsage anImport : importUsage) { J.Import elem = anImport.imports.get(0).getElement(); if (!"*".equals(elem.getQualid().getSimpleName())) { if (elem.isStatic()) { - if (usedStaticWildcardImports.contains(elem.getTypeName())) { + if (usedStaticWildcardImports.contains(elem.getTypeName()) && + !ambiguousStaticImportNames.contains(elem.getQualid().getSimpleName())) { anImport.used = false; changed = true; } @@ -278,7 +280,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon for (int i = 0; i < importGroup.size(); i++) { JRightPadded anImport = importGroup.get(i); if (i == 0 && lastUnusedImportSpace != null && anImport.getElement().getPrefix().getLastWhitespace() - .chars().filter(c -> c == '\n').count() <= 1) { + .chars().filter(c -> c == '\n').count() <= 1) { anImport = anImport.withElement(anImport.getElement().withPrefix(lastUnusedImportSpace)); } imports.add(anImport); @@ -298,6 +300,32 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon return cu; } + private static Set getAmbiguousStaticImportNames(J.CompilationUnit cu) { + Set typesWithWildcardImport = new HashSet<>(); + for (J.Import elem : cu.getImports()) { + if ("*".equals(elem.getQualid().getSimpleName())) { + typesWithWildcardImport.add(elem.getTypeName()); + } + } + Set qualifiedTypes = new HashSet<>(); + for (JavaType.Variable variable : cu.getTypesInUse().getVariables()) { + JavaType.FullyQualified fq = TypeUtils.asFullyQualified(variable.getOwner()); + if (fq != null && typesWithWildcardImport.contains(fq.getFullyQualifiedName())) { + qualifiedTypes.add(fq); + } + } + Set seen = new HashSet<>(); + Set ambiguous = new HashSet<>(); + for (JavaType.FullyQualified fq : qualifiedTypes) { + for (JavaType.Variable member : fq.getMembers()) { + if (!seen.add(member.getName())) { + ambiguous.add(member.getName()); + } + } + } + return ambiguous; + } + private static final Set JAVA_LANG_CLASS_NAMES = new HashSet<>(Arrays.asList( "AbstractMethodError", "Appendable", From c87e8d32365985a9f18eea2b4cf7c6ba5449a69f Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Fri, 6 Sep 2024 16:31:21 -0700 Subject: [PATCH 27/82] Fix incomplete boolean simplification of ternary operators. --- .../gradle/UpgradeDependencyVersion.java | 45 +++++++++++-------- .../gradle/UpgradeDependencyVersionTest.java | 42 +++++++++++++++++ 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index 3f0b19d8f10..2bc3e2216e6 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -15,6 +15,7 @@ */ package org.openrewrite.gradle; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import lombok.Value; @@ -176,14 +177,15 @@ private static boolean isLikelyDependencyConfiguration(Cursor cursor) { public TreeVisitor getScanner(DependencyVersionState acc) { return new GroovyVisitor() { + @Nullable GradleProject gradleProject; @Override public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { - gradleProject = cu.getMarkers().findFirst(GradleProject.class).orElse(null); - if (gradleProject == null) { + if (!cu.getSourcePath().toString().endsWith(".gradle")) { return cu; } + gradleProject = cu.getMarkers().findFirst(GradleProject.class).orElse(null); return super.visitCompilationUnit(cu, ctx); } @@ -252,8 +254,8 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) try { String resolvedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(new GroupArtifact(groupId, artifactId), m.getSimpleName(), newVersion, versionPattern, ctx); - acc.versionPropNameToGA.put(versionVariableName, ga); - acc.gaToNewVersion.put(ga, resolvedVersion); + acc.versionPropNameToGA.put(requireNonNull(versionVariableName), ga); + acc.gaToNewVersion.put(ga, requireNonNull(resolvedVersion)); } catch (MavenDownloadingException e) { acc.gaToNewVersion.put(ga, e); return m; @@ -283,8 +285,10 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) try { String resolvedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(new GroupArtifact(dep.getGroupId(), dep.getArtifactId()), m.getSimpleName(), newVersion, versionPattern, ctx); - acc.versionPropNameToGA.put(versionVariableName, ga); - acc.gaToNewVersion.put(ga, resolvedVersion); + if (resolvedVersion != null) { + acc.versionPropNameToGA.put(versionVariableName, ga); + acc.gaToNewVersion.put(ga, resolvedVersion); + } } catch (MavenDownloadingException e) { acc.gaToNewVersion.put(ga, e); } @@ -300,8 +304,8 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) @Override public TreeVisitor getVisitor(DependencyVersionState acc) { return new TreeVisitor() { - private UpdateGroovy updateGroovy = new UpdateGroovy(acc); - private UpdateProperties updateProperties = new UpdateProperties(acc); + private final UpdateGroovy updateGroovy = new UpdateGroovy(acc); + private final UpdateProperties updateProperties = new UpdateProperties(acc); @Override public boolean isAcceptable(SourceFile sf, ExecutionContext ctx) { @@ -310,11 +314,13 @@ public boolean isAcceptable(SourceFile sf, ExecutionContext ctx) { @Override public @Nullable Tree visit(@Nullable Tree t, ExecutionContext ctx) { - SourceFile sf = (SourceFile) t; - if (updateProperties.isAcceptable(sf, ctx)) { - t = updateProperties.visitNonNull(t, ctx); - } else if (updateGroovy.isAcceptable(sf, ctx)) { - t = updateGroovy.visitNonNull(t, ctx); + if (t instanceof SourceFile) { + SourceFile sf = (SourceFile) t; + if (updateProperties.isAcceptable(sf, ctx)) { + t = updateProperties.visitNonNull(t, ctx); + } else if (updateGroovy.isAcceptable(sf, ctx)) { + t = updateGroovy.visitNonNull(t, ctx); + } } return t; } @@ -359,6 +365,7 @@ public org.openrewrite.properties.tree.Properties visitEntry(Properties.Entry en @RequiredArgsConstructor private class UpdateGroovy extends GroovyVisitor { final DependencyVersionState acc; + @Nullable GradleProject gradleProject; final DependencyMatcher dependencyMatcher = new DependencyMatcher(groupId, artifactId, null); @@ -366,9 +373,6 @@ private class UpdateGroovy extends GroovyVisitor { public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { gradleProject = cu.getMarkers().findFirst(GradleProject.class) .orElse(null); - if (gradleProject == null) { - return cu; - } return super.visitCompilationUnit(cu, ctx); } @@ -377,7 +381,7 @@ public J postVisit(J tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) tree; Map>> variableNames = getCursor().getMessage(VERSION_VARIABLE_KEY); - if (variableNames != null && gradleProject != null) { + if (variableNames != null) { cu = (JavaSourceFile) new UpdateVariable(variableNames, gradleProject).visitNonNull(cu, ctx); } Map> versionUpdates = getCursor().getMessage(NEW_VERSION_KEY); @@ -505,7 +509,7 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution if (!dependencyMatcher.matches((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())) { return m; } - Object scanResult = acc.gaToNewVersion.get(new GroupArtifact((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())); + Object scanResult = acc.gaToNewVersion.get(new GroupArtifact((String) requireNonNull(groupLiteral.getValue()), (String) artifactLiteral.getValue())); if (scanResult instanceof Exception) { return Markup.warn(m, (Exception) scanResult); } @@ -552,9 +556,11 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution } } - @RequiredArgsConstructor + @AllArgsConstructor private class UpdateVariable extends GroovyIsoVisitor { private final Map>> versionVariableNames; + + @Nullable private final GradleProject gradleProject; @Override @@ -666,6 +672,7 @@ public J.Assignment visitAssignment(J.Assignment assignment, ExecutionContext ct public static GradleProject replaceVersion(GradleProject gp, ExecutionContext ctx, GroupArtifactVersion gav, Set configurations) { try { + //noinspection ConstantValue if (gav.getGroupId() == null || gav.getArtifactId() == null) { return gp; } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java index c7aaf02f417..84357fa5952 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java @@ -994,4 +994,46 @@ void exactVersionWithRegexPattern() { ) ); } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.recipe(new UpgradeDependencyVersion("com.fasterxml.jackson.core", "jackson-databind", "2.17.2", null)), + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2") + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } From 83150ba5bef389f9cd28b4edf4b914351eb090f4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 8 Sep 2024 23:55:13 +0200 Subject: [PATCH 28/82] Update receive-pr.yml nullability recipes --- .github/workflows/receive-pr.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/receive-pr.yml b/.github/workflows/receive-pr.yml index 2ffdcd1fa0f..536c2f0765b 100644 --- a/.github/workflows/receive-pr.yml +++ b/.github/workflows/receive-pr.yml @@ -72,13 +72,9 @@ jobs: displayName: Recipe nullability best practices description: Use OpenRewrite internal nullability annotations; drop JetBrains annotations; use `package-info.java` instead. recipeList: - - org.openrewrite.java.ChangeType: - oldFullyQualifiedTypeName: org.jetbrains.annotations.Nullable - newFullyQualifiedTypeName: org.openrewrite.internal.lang.Nullable - - org.openrewrite.java.ChangeType: - oldFullyQualifiedTypeName: jakarta.annotation.Nullable - newFullyQualifiedTypeName: org.openrewrite.internal.lang.Nullable + - org.openrewrite.staticanalysis.NullableOnMethodReturnType - org.openrewrite.java.RemoveAnnotation: annotationPattern: '@org.jetbrains.annotations.NotNull' - org.openrewrite.java.RemoveAnnotation: annotationPattern: '@jakarta.annotation.Nonnull' + - org.openrewrite.java.jspecify.MigrateToJspecify From 2d29e01e1666977b1c2c6cccc9776331585714a9 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 9 Sep 2024 08:38:41 +0000 Subject: [PATCH 29/82] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../java/org/openrewrite/gradle/UpgradeDependencyVersion.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index 2bc3e2216e6..cfc78fe0ca3 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -365,8 +365,10 @@ public org.openrewrite.properties.tree.Properties visitEntry(Properties.Entry en @RequiredArgsConstructor private class UpdateGroovy extends GroovyVisitor { final DependencyVersionState acc; + @Nullable GradleProject gradleProject; + final DependencyMatcher dependencyMatcher = new DependencyMatcher(groupId, artifactId, null); @Override From 0bcb5706ba52277a326d64a52d105979ba60a332 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Mon, 9 Sep 2024 09:20:15 -0500 Subject: [PATCH 30/82] refactor: Update Gradle wrapper (#4478) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 089970a3e72..09e0523f895 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1 From 4a98c3b9c726905be8f33d474605f47c8a758730 Mon Sep 17 00:00:00 2001 From: Nick McKinney Date: Mon, 9 Sep 2024 11:41:13 -0400 Subject: [PATCH 31/82] fixing the LatestRelease support for maven LATEST/RELEASE syntax to be symmetrical --- .../java/org/openrewrite/semver/LatestRelease.java | 12 +++++++----- .../org/openrewrite/semver/LatestReleaseTest.java | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java b/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java index 947cf16e795..18fc5d4b12e 100644 --- a/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java +++ b/rewrite-core/src/main/java/org/openrewrite/semver/LatestRelease.java @@ -100,12 +100,14 @@ static int countVersionParts(String version) { public int compare(@Nullable String currentVersion, String v1, String v2) { if (v1.equalsIgnoreCase(v2)) { return 0; - } - if (v1.equalsIgnoreCase("RELEASE")) { - return v2.equalsIgnoreCase("LATEST") ? -1 : 1; - } - if (v1.equalsIgnoreCase("LATEST")) { + } else if (v1.equalsIgnoreCase("LATEST")) { + return 1; + } else if (v2.equalsIgnoreCase("LATEST")) { + return -1; + } else if (v1.equalsIgnoreCase("RELEASE")) { return 1; + } else if (v2.equalsIgnoreCase("RELEASE")) { + return -1; } String nv1 = normalizeVersion(v1); diff --git a/rewrite-core/src/test/java/org/openrewrite/semver/LatestReleaseTest.java b/rewrite-core/src/test/java/org/openrewrite/semver/LatestReleaseTest.java index b9acf749a8f..fc92187fda7 100644 --- a/rewrite-core/src/test/java/org/openrewrite/semver/LatestReleaseTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/semver/LatestReleaseTest.java @@ -107,10 +107,12 @@ void preReleases() { @Test void releaseOrLatestKeyword_beatsEverything() { assertThat(latestRelease.compare(null, "RELEASE", "1.2.3")).isPositive(); + assertThat(latestRelease.compare(null, "1.2.3", "RELEASE")).isNegative(); assertThat(latestRelease.compare(null, "RELEASE", "999.999.999")).isPositive(); assertThat(latestRelease.compare(null, "RELEASE", "RELEASE")).isZero(); assertThat(latestRelease.compare(null, "LATEST", "1.2.3")).isPositive(); + assertThat(latestRelease.compare(null, "1.2.3", "LATEST")).isNegative(); assertThat(latestRelease.compare(null, "LATEST", "999.999.999")).isPositive(); assertThat(latestRelease.compare(null, "LATEST", "LATEST")).isZero(); From 63ce3b3618673923c9d06f1bda2f88eeffe31211 Mon Sep 17 00:00:00 2001 From: Simon Hutchinson Date: Mon, 9 Sep 2024 23:12:00 +0100 Subject: [PATCH 32/82] Expand GradleDependency Trait to change dependencies in jvm-test-suite DSL & buildscript (#4376) * Create a failing test for dependency change in the jvm-test-suite DSL * Remove unused imports * Add just just enough from the "testing" extension to make the failing test pass * Remove unused import * Remove extends ScriptHandler * Restore accidentally deleted file * Format with Intellij formatter * Add just enough code for closures added to the Gradle Project by the JVM Test Suite plugin to make the failing test pass. * Fix merge * Restore file * Fix formatting * Ensure that ChangeDependency works with dependendencies within jvm-test-suites * Ensure that ChangeDependencyArtifactId works with dependencies defined in jvm-testsuites * Ensure ChangeDependencyClassifier works with jvm-testsuites * Ensure ChangeDependencyClassifier works with dependencies in jvm-testsuite * Ensure that ChangeDependencyExtension works with dependencies defined in jvm-testsuites * Ensure that ChangeDependencyGroupId works with dependencies defined in jvm-testsuites * Ensure that DependencyUseMapNotation works with dependencies defined in jvm-testsuites * Ensure that DependencyUseStringNotation works with dependencies defined in the buildScript * Ensure that RemoveDependency works with dependencies defined in jvm-testsuites * Ensure that UpgradeDependencyVersion works with dependencies defined in jvm-testsuites * Add copyright * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Remove DependencyMatchPredicate.java and fix tests to workk woth gradle Tooling API Note: There are still two failing tests. * Further test fixes * Remove "invalid" no-repo tests. * Handle G.MapLiteral expression in GradleDependency.Matcher * Fall back to dependency dsl matching for ChangeDependencyConfiguration and UpgradeDependencyVersion cases * Minor polish to nullability annotations * Update rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Make sure that all dependency recipes work for freestanding scripts * Fix broken tests --------- Co-authored-by: Simon Hutchinson Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Shannon Pamperl --- .../openrewrite/gradle/ChangeDependency.java | 8 +- .../gradle/ChangeDependencyArtifactId.java | 6 +- .../gradle/ChangeDependencyClassifier.java | 5 +- .../gradle/ChangeDependencyConfiguration.java | 7 +- .../gradle/ChangeDependencyExtension.java | 6 +- .../gradle/ChangeDependencyGroupId.java | 6 +- .../gradle/DependencyUseMapNotation.java | 9 +- .../gradle/DependencyUseStringNotation.java | 9 +- .../openrewrite/gradle/RemoveDependency.java | 8 +- .../gradle/UpgradeDependencyVersion.java | 42 +---- .../gradle/trait/GradleDependency.java | 120 ++++++++---- .../ChangeDependencyArtifactIdTest.java | 168 +++++++++++++++-- .../ChangeDependencyClassifierTest.java | 134 +++++++++++++- .../ChangeDependencyConfigurationTest.java | 157 ++++++++++++++-- .../gradle/ChangeDependencyExtensionTest.java | 133 +++++++++++++- .../gradle/ChangeDependencyGroupIdTest.java | 154 +++++++++++++++- .../gradle/ChangeDependencyTest.java | 49 +++++ .../gradle/DependencyUseMapNotationTest.java | 161 +++++++++++++++-- .../DependencyUseStringNotationTest.java | 171 ++++++++++++++++-- .../gradle/RemoveDependencyTest.java | 91 ++++++++++ .../gradle/UpgradeDependencyVersionTest.java | 135 +++++++------- 21 files changed, 1364 insertions(+), 215 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java index 475ba095104..0ff50885dee 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependency.java @@ -23,6 +23,7 @@ import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.gradle.search.FindGradleProject; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; @@ -30,7 +31,6 @@ import org.openrewrite.groovy.tree.G; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.StringUtils; -import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.maven.MavenDownloadingException; @@ -157,7 +157,6 @@ public Validated validate() { public TreeVisitor getVisitor() { return Preconditions.check(new FindGradleProject(FindGradleProject.SearchCriteria.Marker).getVisitor(), new GroovyIsoVisitor() { final DependencyMatcher depMatcher = requireNonNull(DependencyMatcher.build(oldGroupId + ":" + oldArtifactId).getValue()); - final MethodMatcher dependencyDsl = new MethodMatcher("DependencyHandlerSpec *(..)"); GradleProject gradleProject; @@ -180,7 +179,10 @@ public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionCon @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m)) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!gradleDependencyMatcher.get(getCursor()).isPresent()) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java index 4cbec6a2685..8220bbae2a6 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java @@ -21,6 +21,7 @@ import org.openrewrite.*; import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; @@ -112,7 +113,10 @@ public G visitCompilationUnit(G.CompilationUnit compilationUnit, ExecutionContex @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m) || !(StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration))) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!((gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m)) && (StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration)))) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java index 0e0abaa4954..c2698f806b9 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java @@ -19,6 +19,7 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; @@ -92,7 +93,9 @@ public TreeVisitor getVisitor() { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m) || !(StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration))) { + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!((gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m)) && (StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration)))) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyConfiguration.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyConfiguration.java index c68b552be14..dd2e0275e5c 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyConfiguration.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyConfiguration.java @@ -19,6 +19,7 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; import org.openrewrite.groovy.GroovyVisitor; @@ -91,10 +92,14 @@ public TreeVisitor getVisitor() { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m) || !(StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration))) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!((gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m)) && (StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration)))) { return m; } + DependencyMatcher dependencyMatcher = new DependencyMatcher(groupId, artifactId, null); List args = m.getArguments(); if (args.get(0) instanceof J.Literal) { diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java index fb3054f6847..9847fd08be7 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java @@ -19,6 +19,7 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; @@ -89,7 +90,10 @@ public TreeVisitor getVisitor() { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m) || !(StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration))) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!((gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m)) && (StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration)))) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java index 6fc3a3faae4..6f361ce6b94 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java @@ -21,6 +21,7 @@ import org.openrewrite.*; import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; @@ -112,7 +113,10 @@ public G visitCompilationUnit(G.CompilationUnit compilationUnit, ExecutionContex @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m) || !(StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration))) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!((gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m)) && (StringUtils.isBlank(configuration) || m.getSimpleName().equals(configuration)))) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java index 9f2eb8b146c..88c5e68b977 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java @@ -19,6 +19,7 @@ import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; import org.openrewrite.groovy.GroovyVisitor; @@ -51,12 +52,16 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { - final MethodMatcher dependencyDsl = new MethodMatcher("DependencyHandlerSpec *(..)"); return Preconditions.check(new IsBuildGradle<>(), new GroovyVisitor() { + final MethodMatcher dependencyDsl = new MethodMatcher("DependencyHandlerSpec *(..)"); + @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m)) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!(gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m))) { return m; } m = forBasicString(m); diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseStringNotation.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseStringNotation.java index 4d0e35406a1..b3c17260f90 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseStringNotation.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseStringNotation.java @@ -20,6 +20,7 @@ import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.groovy.GroovyVisitor; import org.openrewrite.groovy.tree.G; import org.openrewrite.java.MethodMatcher; @@ -48,12 +49,16 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { - final MethodMatcher dependencyDsl = new MethodMatcher("DependencyHandlerSpec *(..)"); return Preconditions.check(new IsBuildGradle<>(), new GroovyVisitor() { + final MethodMatcher dependencyDsl = new MethodMatcher("DependencyHandlerSpec *(..)"); + @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (!dependencyDsl.matches(m)) { + + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (!(gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m))) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java index 2335900e825..ae1c4bb520c 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java @@ -22,6 +22,7 @@ import org.openrewrite.*; import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; import org.openrewrite.groovy.GroovyVisitor; @@ -89,7 +90,8 @@ public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { Optional maybeGp = g.getMarkers().findFirst(GradleProject.class); if (!maybeGp.isPresent()) { - return cu; + // Allow modification of freestanding scripts which do not carry a GradleProject marker + return g; } GradleProject gp = maybeGp.get(); @@ -135,7 +137,9 @@ public J visitReturn(J.Return return_, ExecutionContext ctx) { public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (dependencyDsl.matches(m) && (StringUtils.isEmpty(configuration) || configuration.equals(m.getSimpleName()))) { + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if ((gradleDependencyMatcher.get(getCursor()).isPresent() || dependencyDsl.matches(m)) && (StringUtils.isEmpty(configuration) || configuration.equals(m.getSimpleName()))) { Expression firstArgument = m.getArguments().get(0); if (firstArgument instanceof J.Literal || firstArgument instanceof G.GString || firstArgument instanceof G.MapEntry) { //noinspection DataFlowIssue diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index cfc78fe0ca3..15b753b0b58 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -23,6 +23,7 @@ import org.openrewrite.*; import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.gradle.trait.GradleDependency; import org.openrewrite.gradle.util.ChangeStringLiteral; import org.openrewrite.gradle.util.Dependency; import org.openrewrite.gradle.util.DependencyStringNotationConverter; @@ -140,38 +141,7 @@ public DependencyVersionState getInitialValue(ExecutionContext ctx) { return new DependencyVersionState(); } - private static final MethodMatcher DEPENDENCY_DSL_MATCHER = new MethodMatcher("RewriteGradleProject dependencies(groovy.lang.Closure)"); - private static final MethodMatcher DEPENDENCY_CONFIGURATION_MATCHER = new MethodMatcher("DependencyHandlerSpec *(..)"); - - private static boolean isLikelyDependencyConfiguration(Cursor cursor) { - if (!(cursor.getValue() instanceof J.MethodInvocation)) { - return false; - } - J.MethodInvocation m = cursor.getValue(); - if (DEPENDENCY_CONFIGURATION_MATCHER.matches(m)) { - return true; - } - // If it's a configuration created by a plugin, we may not be able to type-attribute it - // In the absence of type-attribution use its presence within a dependencies block to approximate - if (m.getType() != null) { - return false; - } - while (cursor != null) { - if (cursor.getValue() instanceof J.MethodInvocation) { - m = cursor.getValue(); - String methodName = m.getSimpleName(); - if ("constraints".equals(methodName) || "project".equals(methodName) || "modules".equals(methodName) - || "module".equals(methodName) || "file".equals(methodName) || "files".equals(methodName)) { - return false; - } - if (DEPENDENCY_DSL_MATCHER.matches(m)) { - return true; - } - } - cursor = cursor.getParent(); - } - return false; - } + private static final MethodMatcher DEPENDENCY_DSL_MATCHER = new MethodMatcher("DependencyHandlerSpec *(..)"); @Override public TreeVisitor getScanner(DependencyVersionState acc) { @@ -192,7 +162,9 @@ public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (isLikelyDependencyConfiguration(getCursor())) { + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (gradleDependencyMatcher.get(getCursor()).isPresent() || DEPENDENCY_DSL_MATCHER.matches(m)) { if (m.getArguments().get(0) instanceof G.MapEntry) { String groupId = null; String artifactId = null; @@ -406,7 +378,9 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) return method; } J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (isLikelyDependencyConfiguration(getCursor())) { + GradleDependency.Matcher gradleDependencyMatcher = new GradleDependency.Matcher(); + + if (gradleDependencyMatcher.get(getCursor()).isPresent() || DEPENDENCY_DSL_MATCHER.matches(m)) { List depArgs = m.getArguments(); if (depArgs.get(0) instanceof J.Literal || depArgs.get(0) instanceof G.GString || depArgs.get(0) instanceof G.MapEntry) { m = updateDependency(m, ctx); diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java index 901543431e0..1f20c8e92e2 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java @@ -23,6 +23,7 @@ import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.gradle.util.DependencyStringNotationConverter; import org.openrewrite.groovy.tree.G; +import org.openrewrite.internal.StringUtils; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.maven.tree.Dependency; @@ -30,6 +31,7 @@ import org.openrewrite.trait.Trait; import java.util.List; +import java.util.stream.Collectors; import static org.openrewrite.internal.StringUtils.matchesGlob; @@ -41,18 +43,26 @@ public class GradleDependency implements Trait { ResolvedDependency resolvedDependency; public static class Matcher extends GradleTraitMatcher { + @Nullable + protected String configuration; + @Nullable protected String groupId; @Nullable protected String artifactId; - public Matcher groupId(String groupId) { + public Matcher configuration(@Nullable String configuration) { + this.configuration = configuration; + return this; + } + + public Matcher groupId(@Nullable String groupId) { this.groupId = groupId; return this; } - public Matcher artifactId(String artifactId) { + public Matcher artifactId(@Nullable String artifactId) { this.artifactId = artifactId; return this; } @@ -63,19 +73,27 @@ public Matcher artifactId(String artifactId) { if (object instanceof J.MethodInvocation) { J.MethodInvocation methodInvocation = (J.MethodInvocation) object; + if (!withinDependenciesBlock(cursor)) { + return null; + } + GradleProject gradleProject = getGradleProject(cursor); if (gradleProject == null) { return null; } - GradleDependencyConfiguration configuration = gradleProject.getConfiguration(methodInvocation.getSimpleName()); - if (configuration == null) { + GradleDependencyConfiguration gdc = getConfiguration(gradleProject, methodInvocation); + if (gdc == null) { + return null; + } + + if (!(StringUtils.isBlank(configuration) || methodInvocation.getSimpleName().equals(configuration))) { return null; } org.openrewrite.gradle.util.Dependency dependency = null; Expression argument = methodInvocation.getArguments().get(0); - if (argument instanceof J.Literal || argument instanceof G.GString || argument instanceof G.MapEntry) { + if (argument instanceof J.Literal || argument instanceof G.GString || argument instanceof G.MapEntry || argument instanceof G.MapLiteral) { dependency = parseDependency(methodInvocation.getArguments()); } else if (argument instanceof J.MethodInvocation && (((J.MethodInvocation) argument).getSimpleName().equals("platform") || @@ -87,8 +105,8 @@ public Matcher artifactId(String artifactId) { return null; } - if (configuration.isCanBeResolved()) { - for (ResolvedDependency resolvedDependency : configuration.getResolved()) { + if (gdc.isCanBeResolved()) { + for (ResolvedDependency resolvedDependency : gdc.getResolved()) { if ((groupId == null || matchesGlob(resolvedDependency.getGroupId(), groupId)) && (artifactId == null || matchesGlob(resolvedDependency.getArtifactId(), artifactId))) { Dependency req = resolvedDependency.getRequested(); @@ -99,7 +117,7 @@ public Matcher artifactId(String artifactId) { } } } else { - for (GradleDependencyConfiguration transitiveConfiguration : gradleProject.configurationsExtendingFrom(configuration, true)) { + for (GradleDependencyConfiguration transitiveConfiguration : gradleProject.configurationsExtendingFrom(gdc, true)) { if (transitiveConfiguration.isCanBeResolved()) { for (ResolvedDependency resolvedDependency : transitiveConfiguration.getResolved()) { if ((groupId == null || matchesGlob(resolvedDependency.getGroupId(), groupId)) && @@ -119,6 +137,30 @@ public Matcher artifactId(String artifactId) { return null; } + private static @Nullable GradleDependencyConfiguration getConfiguration(GradleProject gradleProject, J.MethodInvocation methodInvocation) { + String methodName = methodInvocation.getSimpleName(); + if (methodName.equals("classpath")) { + return gradleProject.getBuildscript().getConfiguration(methodName); + } else { + return gradleProject.getConfiguration(methodName); + } + } + + private boolean withinDependenciesBlock(Cursor cursor) { + Cursor parentCursor = cursor.getParent(); + while (parentCursor != null) { + if (parentCursor.getValue() instanceof J.MethodInvocation) { + J.MethodInvocation m = parentCursor.getValue(); + if (m.getSimpleName().equals("dependencies")) { + return true; + } + } + parentCursor = parentCursor.getParent(); + } + + return false; + } + private org.openrewrite.gradle.util.@Nullable Dependency parseDependency(List arguments) { Expression argument = arguments.get(0); if (argument instanceof J.Literal) { @@ -129,39 +171,49 @@ public Matcher artifactId(String artifactId) { if (strings.size() >= 2 && strings.get(0) instanceof J.Literal && ((J.Literal) strings.get(0)).getValue() != null) { return DependencyStringNotationConverter.parse((String) ((J.Literal) strings.get(0)).getValue()); } + } else if (argument instanceof G.MapLiteral) { + List mapEntryExpressions = ((G.MapLiteral) argument).getElements() + .stream() + .map(e -> (Expression) e) + .collect(Collectors.toList()); + return getMapEntriesDependency(mapEntryExpressions); } else if (argument instanceof G.MapEntry) { - String group = null; - String artifact = null; + return getMapEntriesDependency(arguments); + } - for (Expression e : arguments) { - if (!(e instanceof G.MapEntry)) { - continue; - } - G.MapEntry arg = (G.MapEntry) e; - if (!(arg.getKey() instanceof J.Literal) || !(arg.getValue() instanceof J.Literal)) { - continue; - } - J.Literal key = (J.Literal) arg.getKey(); - J.Literal value = (J.Literal) arg.getValue(); - if (!(key.getValue() instanceof String) || !(value.getValue() instanceof String)) { - continue; - } - String keyValue = (String) key.getValue(); - if ("group".equals(keyValue)) { - group = (String) value.getValue(); - } else if ("name".equals(keyValue)) { - artifact = (String) value.getValue(); - } - } + return null; + } - if (group == null || artifact == null) { - return null; + private static org.openrewrite.gradle.util.@Nullable Dependency getMapEntriesDependency(List arguments) { + String group = null; + String artifact = null; + + for (Expression e : arguments) { + if (!(e instanceof G.MapEntry)) { + continue; + } + G.MapEntry arg = (G.MapEntry) e; + if (!(arg.getKey() instanceof J.Literal) || !(arg.getValue() instanceof J.Literal)) { + continue; } + J.Literal key = (J.Literal) arg.getKey(); + J.Literal value = (J.Literal) arg.getValue(); + if (!(key.getValue() instanceof String) || !(value.getValue() instanceof String)) { + continue; + } + String keyValue = (String) key.getValue(); + if ("group".equals(keyValue)) { + group = (String) value.getValue(); + } else if ("name".equals(keyValue)) { + artifact = (String) value.getValue(); + } + } - return new org.openrewrite.gradle.util.Dependency(group, artifact, null, null, null); + if (group == null || artifact == null) { + return null; } - return null; + return new org.openrewrite.gradle.util.Dependency(group, artifact, null, null, null); } } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyArtifactIdTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyArtifactIdTest.java index f78487f13c4..cc1a24e6141 100755 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyArtifactIdTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyArtifactIdTest.java @@ -21,6 +21,7 @@ import org.openrewrite.DocumentExample; import org.openrewrite.Issue; import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.assertj.core.api.Assertions.assertThat; @@ -29,6 +30,11 @@ class ChangeDependencyArtifactIdTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.beforeRecipe(withToolingApi()); + } + @DocumentExample @Test void worksWithEmptyStringConfig() { @@ -69,8 +75,7 @@ void worksWithEmptyStringConfig() { @CsvSource(value = {"org.openrewrite:rewrite-core", "*:*"}, delimiterString = ":") void findDependency(String group, String artifact) { rewriteRun( - spec -> spec.recipe(new ChangeDependencyArtifactId(group, artifact, "dewrite-core", null)) - .beforeRecipe(withToolingApi()), + spec -> spec.recipe(new ChangeDependencyArtifactId(group, artifact, "dewrite-core", null)), buildGradle( """ plugins { @@ -148,11 +153,10 @@ void findMapStyleDependency(String group, String artifact) { ); } - @ParameterizedTest - @CsvSource(value = {"org.openrewrite:rewrite-core", "*:*"}, delimiterString = ":") - void worksWithoutVersion(String group, String artifact) { + @Test + void worksWithoutVersion() { rewriteRun( - spec -> spec.recipe(new ChangeDependencyArtifactId(group, artifact, "dewrite-core", null)), + spec -> spec.recipe(new ChangeDependencyArtifactId("org.openrewrite", "rewrite-core", "rewrite-gradle", null)), buildGradle( """ plugins { @@ -164,10 +168,11 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api 'org.openrewrite:rewrite-core' api "org.openrewrite:rewrite-core" - api group: 'org.openrewrite', name: 'dewrite-core' - api group: "org.openrewrite", name: "dewrite-core" + api group: 'org.openrewrite', name: 'rewrite-gradle' + api group: "org.openrewrite", name: "rewrite-gradle" } """, """ @@ -180,16 +185,17 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { - api 'org.openrewrite:dewrite-core' - api "org.openrewrite:dewrite-core" - api group: 'org.openrewrite', name: 'dewrite-core' - api group: "org.openrewrite", name: "dewrite-core" + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) + api 'org.openrewrite:rewrite-gradle' + api "org.openrewrite:rewrite-gradle" + api group: 'org.openrewrite', name: 'rewrite-gradle' + api group: "org.openrewrite", name: "rewrite-gradle" } """ ) ); } - + @ParameterizedTest @CsvSource(value = {"org.openrewrite:rewrite-core", "*:*"}, delimiterString = ":") void worksWithClassifier(String group, String artifact) { @@ -297,12 +303,28 @@ void worksWithGString() { spec -> spec.recipe(new ChangeDependencyArtifactId("javax.validation", "validation-api", "jakarta.validation-api", null)), buildGradle( """ + plugins { + id 'java-library' + } + + repositories { + mavenCentral() + } + dependencies { def jakartaVersion = "2.0.1.Final" implementation "javax.validation:validation-api:${jakartaVersion}" } """, """ + plugins { + id 'java-library' + } + + repositories { + mavenCentral() + } + dependencies { def jakartaVersion = "2.0.1.Final" implementation "javax.validation:jakarta.validation-api:${jakartaVersion}" @@ -349,4 +371,124 @@ implementation platform("org.optaplanner:timefold-solver-bom:9.37.0.Final") ) ); } + + @Test + void worksWithDependencyDefinedInJvmTestSuite() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyArtifactId("org.springframework.boot", "spring-boot-starter", "new-starter", "")), + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:2.5.4' + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.springframework.boot:new-starter:2.5.4' + } + } + } + } + """ + ) + ); + } + + @Test + void worksWithDependencyDefinedInBuildScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyArtifactId("org.springframework.boot", "spring-boot-starter", "new-starter", "")), + buildGradle( + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.springframework.boot:spring-boot-starter:2.5.4' + } + } + """, + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.springframework.boot:new-starter:2.5.4' + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyArtifactId("org.springframework.boot", "spring-boot-starter", "new-starter", null)), + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.springframework.boot:spring-boot-starter:2.5.4") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.springframework.boot:new-starter:2.5.4") + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyClassifierTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyClassifierTest.java index caad9c5bccb..fe95d396a8d 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyClassifierTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyClassifierTest.java @@ -19,11 +19,19 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; class ChangeDependencyClassifierTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.beforeRecipe(withToolingApi()); + } + @DocumentExample @Test void worksWithEmptyStringConfig() { @@ -152,8 +160,9 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) + api group: 'org.openrewrite', name: 'rewrite-core', classifier: 'javadoc' api group: 'org.openrewrite', name: 'rewrite-core', classifier: 'javadoc' - api group: "org.openrewrite", name: "rewrite-core", classifier: "javadoc" } """, """ @@ -166,8 +175,9 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) + api group: 'org.openrewrite', name: 'rewrite-core', classifier: 'classified' api group: 'org.openrewrite', name: 'rewrite-core', classifier: 'classified' - api group: "org.openrewrite", name: "rewrite-core", classifier: "classified" } """ ) @@ -411,4 +421,124 @@ void noNewClassifier_2(String group, String artifact) { ) ); } + + @Test + void worksWithDependencyDefinedInJvmTestSuite() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyClassifier("org.openrewrite", "*", "classified", "")), + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.openrewrite:rewrite-gradle:latest.release:javadoc' + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.openrewrite:rewrite-gradle:latest.release:classified' + } + } + } + } + """ + ) + ); + } + + @Test + void worksWithDependencyDefinedInBuildScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyClassifier("org.openrewrite", "*", "classified", "")), + buildGradle( + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.openrewrite:rewrite-gradle:latest.release:javadoc' + } + } + """, + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.openrewrite:rewrite-gradle:latest.release:classified' + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyClassifier("org.openrewrite", "*", "classified", "")), + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.openrewrite:rewrite-gradle:latest.release:javadoc") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.openrewrite:rewrite-gradle:latest.release:classified") + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java index 47fdc0c9e31..d5739e7c159 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java @@ -19,11 +19,21 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.Assertions.settingsGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; +import static org.openrewrite.java.Assertions.mavenProject; class ChangeDependencyConfigurationTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.beforeRecipe(withToolingApi()); + } + @DocumentExample @Test void changeConfiguration() { @@ -210,21 +220,55 @@ void changeGStringStyleDependency(String group, String artifact) { } @ParameterizedTest - @CsvSource(value = {"*:a", "*:*"}, delimiterString = ":") + @CsvSource(value = {"*:project2", "*:*"}, delimiterString = ":") void worksForProjectDependencies(String group, String artifact) { rewriteRun( spec -> spec.recipe(new ChangeDependencyConfiguration(group, artifact, "implementation", null)), - buildGradle( - """ - dependencies { - compile project(":a") - } - """, - """ - dependencies { - implementation project(":a") - } + mavenProject("root", + buildGradle( + "" + ), + settingsGradle( """ + include "project1" + include "project2" + """ + ), + mavenProject("project1", + buildGradle( + """ + plugins { + id 'java-library' + } + + repositories { + mavenCentral() + } + + dependencies { + api project(":project2") + } + """, + """ + plugins { + id 'java-library' + } + + repositories { + mavenCentral() + } + + dependencies { + implementation project(":project2") + } + """ + ) + ), + mavenProject("project2", + buildGradle( + "" + ) + ) ) ); } @@ -265,4 +309,95 @@ void onlyChangeSpecificDependency() { ) ); } + + @Test + void worksWithDependencyDefinedInJvmTestSuite() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyConfiguration("org.openrewrite", "*", "implementation", "")), + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + runtimeOnly 'org.openrewrite:rewrite-gradle:latest.release' + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.openrewrite:rewrite-gradle:latest.release' + } + } + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyConfiguration("org.openrewrite", "*", "implementation", "")), + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + runtimeOnly 'org.openrewrite:rewrite-gradle:latest.release' + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation 'org.openrewrite:rewrite-gradle:latest.release' + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyExtensionTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyExtensionTest.java index 8ac656b75a5..aa70915d18a 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyExtensionTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyExtensionTest.java @@ -19,12 +19,19 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; class ChangeDependencyExtensionTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.beforeRecipe(withToolingApi()); + } + @DocumentExample @Test void worksWithEmptyStringConfig() { @@ -153,8 +160,7 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { - api 'org.openrewrite:rewrite-core@jar' - api "org.openrewrite:rewrite-core@jar" + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api group: 'org.openrewrite', name: 'rewrite-core', ext: 'jar' api group: "org.openrewrite", name: "rewrite-core", ext: "jar" } @@ -169,8 +175,7 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { - api 'org.openrewrite:rewrite-core@war' - api "org.openrewrite:rewrite-core@war" + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api group: 'org.openrewrite', name: 'rewrite-core', ext: 'war' api group: "org.openrewrite", name: "rewrite-core", ext: "war" } @@ -220,4 +225,124 @@ void worksWithClassifier(String group, String artifact) { ) ); } + + @Test + void worksWithDependencyDefinedInJvmTestSuite() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyExtension("org.openrewrite", "*", "war", "")), + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.openrewrite:rewrite-gradle:latest.integration@jar' + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.openrewrite:rewrite-gradle:latest.integration@war' + } + } + } + } + """ + ) + ); + } + + @Test + void worksWithDependencyDefinedInBuildScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyExtension("org.openrewrite", "*", "war", "")), + buildGradle( + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.openrewrite:rewrite-gradle:latest.integration@jar' + } + } + """, + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.openrewrite:rewrite-gradle:latest.integration@war' + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyExtension("org.openrewrite", "*", "war", "")), + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.openrewrite:rewrite-gradle:latest.integration@jar") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.openrewrite:rewrite-gradle:latest.integration@war") + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyGroupIdTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyGroupIdTest.java index b473f3b0df6..b872c4560f0 100755 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyGroupIdTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyGroupIdTest.java @@ -21,6 +21,7 @@ import org.openrewrite.DocumentExample; import org.openrewrite.Issue; import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.assertj.core.api.Assertions.assertThat; @@ -29,6 +30,11 @@ class ChangeDependencyGroupIdTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.beforeRecipe(withToolingApi()); + } + @DocumentExample @Test void worksWithEmptyStringConfig() { @@ -69,8 +75,7 @@ void worksWithEmptyStringConfig() { @CsvSource(value = {"org.openrewrite:rewrite-core", "*:*"}, delimiterString = ":") void findDependency(String group, String artifact) { rewriteRun( - spec -> spec.recipe(new ChangeDependencyGroupId(group, artifact, "org.dewrite", null)) - .beforeRecipe(withToolingApi()), + spec -> spec.recipe(new ChangeDependencyGroupId(group, artifact, "org.dewrite", null)), buildGradle( """ plugins { @@ -148,11 +153,10 @@ void findMapStyleDependency(String group, String artifact) { ); } - @ParameterizedTest - @CsvSource(value = {"org.openrewrite:rewrite-core", "*:*"}, delimiterString = ":") - void worksWithoutVersion(String group, String artifact) { + @Test + void worksWithoutVersion() { rewriteRun( - spec -> spec.recipe(new ChangeDependencyGroupId(group, artifact, "org.dewrite", null)), + spec -> spec.recipe(new ChangeDependencyGroupId("org.openrewrite", "rewrite-core", "org.dewrite", null)), buildGradle( """ plugins { @@ -164,6 +168,7 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api 'org.openrewrite:rewrite-core' api "org.openrewrite:rewrite-core" api group: 'org.openrewrite', name: 'rewrite-core' @@ -180,6 +185,7 @@ void worksWithoutVersion(String group, String artifact) { } dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api 'org.dewrite:rewrite-core' api "org.dewrite:rewrite-core" api group: 'org.dewrite', name: 'rewrite-core' @@ -297,12 +303,28 @@ void worksWithGString() { spec -> spec.recipe(new ChangeDependencyGroupId("javax.validation", "validation-api", "jakarta.validation", null)), buildGradle( """ + plugins { + id 'java-library' + } + + repositories { + mavenCentral() + } + dependencies { def jakartaVersion = "2.0.1.Final" implementation "javax.validation:validation-api:${jakartaVersion}" } """, """ + plugins { + id 'java-library' + } + + repositories { + mavenCentral() + } + dependencies { def jakartaVersion = "2.0.1.Final" implementation "jakarta.validation:validation-api:${jakartaVersion}" @@ -349,4 +371,124 @@ implementation platform("ai.timefold.solver:optaplanner-bom:9.37.0.Final") ) ); } + + @Test + void worksWithDependencyDefinedInJvmTestSuite() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyGroupId("org.springframework.boot", "spring-boot-starter", "org.newboot", "")), + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.springframework.boot:spring-boot-starter:2.5.4' + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'org.newboot:spring-boot-starter:2.5.4' + } + } + } + } + """ + ) + ); + } + + @Test + void worksWithDependencyDefinedInBuildScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyGroupId("org.springframework.boot", "spring-boot-starter", "org.newboot", "")), + buildGradle( + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.springframework.boot:spring-boot-starter:2.5.4' + } + } + """, + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.newboot:spring-boot-starter:2.5.4' + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + spec -> spec.recipe(new ChangeDependencyGroupId("org.springframework.boot", "spring-boot-starter", "org.newboot", "")), + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.springframework.boot:spring-boot-starter:2.5.4") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.newboot:spring-boot-starter:2.5.4") + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyTest.java index e7198d03590..decf3758ce9 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyTest.java @@ -396,4 +396,53 @@ void warPluginProvidedConfigurations() { ) ); } + + @Test + void relocateDependencyInJvmTestSuite() { + rewriteRun( + spec -> spec.recipe(new ChangeDependency("commons-lang", "commons-lang", "org.apache.commons", "commons-lang3", "3.11.x", null, null)), + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation "commons-lang:commons-lang:2.6" + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation "org.apache.commons:commons-lang3:3.11" + } + } + } + } + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseMapNotationTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseMapNotationTest.java index 2358899c6dc..b91a42ea085 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseMapNotationTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseMapNotationTest.java @@ -22,12 +22,14 @@ import org.openrewrite.test.RewriteTest; import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; class DependencyUseMapNotationTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new DependencyUseMapNotation()); + spec.beforeRecipe(withToolingApi()) + .recipe(new DependencyUseMapNotation()); } @DocumentExample @@ -46,7 +48,7 @@ void basicString() { dependencies { api('org.openrewrite:rewrite-core:latest.release') - implementation "group:artifact:version" + implementation "org.openrewrite:rewrite-core:latest.release" } """, """ @@ -60,7 +62,7 @@ void basicString() { dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') - implementation group: 'group', name: 'artifact', version: 'version' + implementation group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release' } """ ) @@ -83,7 +85,7 @@ void withGString() { def version = "latest.release" dependencies { api("org.openrewrite:rewrite-core:$version") - implementation "group:artifact:$version" + implementation "org.openrewrite:rewrite-gradle:$version" } """, """ @@ -98,7 +100,7 @@ void withGString() { def version = "latest.release" dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: version) - implementation group: 'group', name: 'artifact', version: version + implementation group: 'org.openrewrite', name: 'rewrite-gradle', version: version } """ ) @@ -120,10 +122,10 @@ void withExclusion() { dependencies { api("org.openrewrite:rewrite-core:latest.release") { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } - implementation "group:artifact:version", { - exclude group: "group2", module: "artifact2" + implementation "org.openrewrite:rewrite-gradle:latest.release", { + exclude group: "org.openrewrite", module: "rewrite-core" } } """, @@ -138,10 +140,10 @@ void withExclusion() { dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } - implementation group: 'group', name: 'artifact', version: 'version', { - exclude group: "group2", module: "artifact2" + implementation group: 'org.openrewrite', name: 'rewrite-gradle', version: 'latest.release', { + exclude group: "org.openrewrite", module: "rewrite-core" } } """ @@ -162,12 +164,13 @@ void withGStringAndExclusion() { mavenCentral() } + def version = "latest.release" dependencies { api("org.openrewrite:rewrite-core:$version") { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } - implementation "group:artifact:$version", { - exclude group: "group2", module: "artifact2" + implementation "org.openrewrite:rewrite-gradle:$version", { + exclude group: "org.openrewrite", module: "rewrite-core" } } """, @@ -180,12 +183,13 @@ void withGStringAndExclusion() { mavenCentral() } + def version = "latest.release" dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: version) { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } - implementation group: 'group', name: 'artifact', version: version, { - exclude group: "group2", module: "artifact2" + implementation group: 'org.openrewrite', name: 'rewrite-gradle', version: version, { + exclude group: "org.openrewrite", module: "rewrite-core" } } """ @@ -208,7 +212,8 @@ void withoutVersion() { } dependencies { - implementation "org.openrewrite:rewrite-core" + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) + implementation "org.openrewrite.recipe:rewrite-logging-frameworks" } """, """ @@ -221,7 +226,8 @@ void withoutVersion() { } dependencies { - implementation group: 'org.openrewrite', name: 'rewrite-core' + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) + implementation group: 'org.openrewrite.recipe', name: 'rewrite-logging-frameworks' } """ ) @@ -261,4 +267,121 @@ void withClassifierAndExtension() { ) ); } + + @Test + void worksWithDependencyDefinedInJvmTestSuite() { + rewriteRun( + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation('org.openrewrite:rewrite-core:latest.release') + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') + } + } + } + } + """ + ) + ); + } + + @Test + void worksWithDependencyDefinedInBuildScript() { + rewriteRun( + buildGradle( + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'org.openrewrite:rewrite-core:latest.release' + } + } + """, + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release' + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.openrewrite:rewrite-core:latest.release") + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseStringNotationTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseStringNotationTest.java index f82f9630ab7..8963f259414 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseStringNotationTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/DependencyUseStringNotationTest.java @@ -21,12 +21,14 @@ import org.openrewrite.test.RewriteTest; import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; class DependencyUseStringNotationTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new DependencyUseStringNotation()); + spec.beforeRecipe(withToolingApi()) + .recipe(new DependencyUseStringNotation()); } @DocumentExample @@ -39,9 +41,13 @@ void basicMap() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') - implementation group: 'group', name: 'artifact', version: 'version' + implementation group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release' } """, """ @@ -49,9 +55,13 @@ void basicMap() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api("org.openrewrite:rewrite-core:latest.release") - implementation "group:artifact:version" + implementation "org.openrewrite:rewrite-core:latest.release" } """ ) @@ -66,10 +76,14 @@ void withClassifier() { plugins { id 'java-library' } - + + repositories { + mavenCentral() + } + dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release', classifier: 'sources') - implementation group: 'group', name: 'artifact', version: 'version', classifier: 'sources' + implementation group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release', classifier: 'sources' } """, """ @@ -77,9 +91,13 @@ void withClassifier() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api("org.openrewrite:rewrite-core:latest.release:sources") - implementation "group:artifact:version:sources" + implementation "org.openrewrite:rewrite-core:latest.release:sources" } """ ) @@ -95,6 +113,10 @@ void basicMapLiteral() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api([group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release']) } @@ -104,6 +126,10 @@ void basicMapLiteral() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api("org.openrewrite:rewrite-core:latest.release") } @@ -121,10 +147,14 @@ void withGString() { id 'java-library' } + repositories { + mavenCentral() + } + def version = "latest.release" dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: version) - implementation group: 'group', name: 'artifact', version: version + implementation group: 'org.openrewrite', name: 'rewrite-core', version: version } """, """ @@ -132,10 +162,14 @@ void withGString() { id 'java-library' } + repositories { + mavenCentral() + } + def version = "latest.release" dependencies { api("org.openrewrite:rewrite-core:$version") - implementation "group:artifact:$version" + implementation "org.openrewrite:rewrite-core:$version" } """ ) @@ -151,10 +185,14 @@ void withGStringLiteral() { id 'java-library' } + repositories { + mavenCentral() + } + def version = "latest.release" dependencies { api(group: 'org.openrewrite', name: 'rewrite-core', version: "$version") - implementation group: 'group', name: 'artifact', version: "$version" + implementation group: 'org.openrewrite', name: 'rewrite-core', version: "$version" } """, """ @@ -162,10 +200,14 @@ void withGStringLiteral() { id 'java-library' } + repositories { + mavenCentral() + } + def version = "latest.release" dependencies { api("org.openrewrite:rewrite-core:$version") - implementation "group:artifact:$version" + implementation "org.openrewrite:rewrite-core:$version" } """ ) @@ -180,20 +222,30 @@ void withoutVersion() { plugins { id 'java-library' } - + + repositories { + mavenCentral() + } + dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api(group: "org.openrewrite", name: "rewrite-core") - implementation group: "group", name: "artifact" + implementation group: "org.openrewrite", name: "rewrite-core" } """, """ plugins { id 'java-library' } + + repositories { + mavenCentral() + } dependencies { + implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:latest.release")) api("org.openrewrite:rewrite-core") - implementation "group:artifact" + implementation "org.openrewrite:rewrite-core" } """ ) @@ -209,9 +261,13 @@ void withExclusion() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api(group: "org.openrewrite", name: "rewrite-core", version: "latest.release") { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } } """, @@ -220,9 +276,13 @@ void withExclusion() { id 'java-library' } + repositories { + mavenCentral() + } + dependencies { api("org.openrewrite:rewrite-core:latest.release") { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } } """ @@ -239,10 +299,14 @@ void withGStringExclusion() { id 'java-library' } + repositories { + mavenCentral() + } + def version = "latest.release" dependencies { api(group: "org.openrewrite", name: "rewrite-core", version: version) { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } } """, @@ -251,14 +315,87 @@ void withGStringExclusion() { id 'java-library' } + repositories { + mavenCentral() + } + def version = "latest.release" dependencies { api("org.openrewrite:rewrite-core:$version") { - exclude group: "group", module: "artifact" + exclude group: "org.openrewrite", module: "rewrite-gradle" } } """ ) ); } + + @Test + void worksWithDependencyDefinedInBuildScript() { + rewriteRun( + buildGradle( + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') + } + } + """, + """ + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath("org.openrewrite:rewrite-core:latest.release") + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation(group: 'org.openrewrite', name: 'rewrite-core', version: 'latest.release') + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.openrewrite:rewrite-core:latest.release") + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/RemoveDependencyTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/RemoveDependencyTest.java index 9037b55cd06..e98dcb0fdd6 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/RemoveDependencyTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/RemoveDependencyTest.java @@ -455,4 +455,95 @@ void removeBuildscriptDependency() { ) ); } + + @Test + void removeDependencyDefinedInJvmTestSuite() { + rewriteRun( + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation "org.springframework.boot:spring-boot-starter-web:2.7.0" + implementation "org.junit.vintage:junit-vintage-engine:5.6.2" + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation "org.junit.vintage:junit-vintage-engine:5.6.2" + } + } + } + } + """ + ) + ); + } + + @Test + void dependenciesBlockInFreestandingScript() { + rewriteRun( + buildGradle( + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation("org.springframework.boot:spring-boot-starter-web:2.7.0") + implementation "org.junit.vintage:junit-vintage-engine:5.6.2" + } + """, + """ + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + dependencies { + implementation "org.junit.vintage:junit-vintage-engine:5.6.2" + } + """, + spec -> spec.path("dependencies.gradle") + ), + buildGradle( + """ + plugins { + id("java") + } + apply from: 'dependencies.gradle' + """ + ) + ); + } } diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java index 84357fa5952..fc236b8d3be 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java @@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.Assertions.settingsGradle; import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; import static org.openrewrite.properties.Assertions.properties; @@ -97,64 +98,6 @@ void guava() { ); } - @Test - void noRepos() { - rewriteRun( - buildGradle( - """ - plugins { - id 'java-library' - } - - dependencies { - compileOnly 'com.google.guava:guava:29.0-jre' - } - """, - """ - plugins { - id 'java-library' - } - - dependencies { - /*~~(com.google.guava:guava failed. Unable to download metadata.)~~>*/compileOnly 'com.google.guava:guava:29.0-jre' - } - """ - ) - ); - } - - @Test - void noReposProperties() { - rewriteRun( - properties( - """ - guavaVersion=29.0-jre - """, - spec -> spec.path("gradle.properties") - ), - buildGradle( - """ - plugins { - id 'java-library' - } - - dependencies { - compileOnly "com.google.guava:guava:${guavaVersion}" - } - """, - """ - plugins { - id 'java-library' - } - - dependencies { - /*~~(com.google.guava:guava failed. Unable to download metadata.)~~>*/compileOnly "com.google.guava:guava:${guavaVersion}" - } - """ - ) - ); - } - @Test void updateVersionInVariable() { rewriteRun( @@ -609,7 +552,6 @@ void versionInParentAndMultiModulePropertiesFiles() { ); } - @Test void versionInParentSubprojectDefinitionWithPropertiesFiles() { rewriteRun( @@ -636,7 +578,9 @@ void versionInParentSubprojectDefinitionWithPropertiesFiles() { repositories { mavenCentral() } - + + apply plugin: "java-library" + dependencies { implementation ("com.google.guava:guava:$guavaVersion") } @@ -644,9 +588,20 @@ void versionInParentSubprojectDefinitionWithPropertiesFiles() { """, spec -> spec.path("build.gradle") ), + settingsGradle( + """ + rootProject.name = 'my-project' + include("moduleA") + """ + ), buildGradle( """ - dependencies { + plugins { + id 'java-library' + } + + repositories { + mavenCentral() } """, spec -> spec.path("moduleA/build.gradle") @@ -867,6 +822,11 @@ void unknownConfiguration() { id 'java' id "org.hidetake.swagger.generator" version "2.18.2" } + + repositories { + mavenCentral() + } + dependencies { swaggerCodegen "org.openapitools:openapi-generator-cli:5.2.0" } @@ -876,6 +836,11 @@ void unknownConfiguration() { id 'java' id "org.hidetake.swagger.generator" version "2.18.2" } + + repositories { + mavenCentral() + } + dependencies { swaggerCodegen "org.openapitools:openapi-generator-cli:5.2.1" } @@ -995,6 +960,54 @@ void exactVersionWithRegexPattern() { ); } + @Test + void upgradesDependencyVersionDefinedInJvmTestSuite() { + rewriteRun( + buildGradle( + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'com.google.guava:guava:29.0-jre' + } + } + } + } + """, + """ + plugins { + id "java-library" + id 'jvm-test-suite' + } + + repositories { + mavenCentral() + } + + testing { + suites { + test { + dependencies { + implementation 'com.google.guava:guava:30.1.1-jre' + } + } + } + } + """ + ) + ); + } + @Test void dependenciesBlockInFreestandingScript() { rewriteRun( From 22369816c82e485974bdd80b4885af2d85003ab2 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Tue, 10 Sep 2024 11:10:53 +0200 Subject: [PATCH 33/82] `RemoveUnusedImports` should not remove imports for used nested classes (#4479) * Add testcases for nested imports * Apply suggestions from code review * Move the fix to RemoveUnusedImports recipe * Revert javadoc * Remove unnecessary cast --------- Co-authored-by: Tim te Beek --- .../java/RemoveUnusedImportsTest.java | 77 +++++++++++++++++++ .../openrewrite/java/RemoveUnusedImports.java | 4 +- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java index 70637a63134..cae81b2e3b7 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java @@ -1906,4 +1906,81 @@ public class Bar { ) ); } + + @Issue("https://github.com/openrewrite/rewrite/issues/3283") + @Test + void nestedImport() { + rewriteRun( + java( + """ + package foo; + + public interface J { + final class One implements J {} + final class Two implements J {} + final class Three implements J {} + final class Four implements J {} + final class Five implements J {} + final class Six implements J {} + } + """ + ), + java( + """ + package bar; + + import foo.J; + import foo.J.*; + + class Quz { + void test() { + J j = null; + One one = null; + Two two = null; + Three three = null; + Four four = null; + Five five = null; + Six six = null; + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite/issues/3283") + @Test + void nestedImportStaticInnerClass() { + rewriteRun( + java( + """ + package com.a.b.c; + public final class ParentBaseClass { + public static abstract class BaseImplClass { + void foo() { + } + } + } + """ + ), + java( + """ + import com.a.b.c.ParentBaseClass.*; + public class MyClass extends BaseImplClass { + @Override + public void foo() { + } + } + """, + """ + import com.a.b.c.ParentBaseClass.BaseImplClass; + public class MyClass extends BaseImplClass { + @Override + public void foo() { + } + } + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java b/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java index 856ca707f2f..86e99d5f313 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java @@ -226,7 +226,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon // add each unfolded import combinedTypes.stream().map(JavaType.FullyQualified::getClassName).sorted().distinct().forEach(type -> anImport.imports.add(new JRightPadded<>(elem - .withQualid(qualid.withName(name.withSimpleName(type))) + .withQualid(qualid.withName(name.withSimpleName(type.substring(type.lastIndexOf('.') + 1)))) .withPrefix(Space.format("\n")), Space.EMPTY, Markers.EMPTY)) ); @@ -236,7 +236,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon changed = true; } else { - usedWildcardImports.add(elem.getPackageName()); + usedWildcardImports.add(elem.getQualid().getTarget().toString()); } } else if (combinedTypes.stream().noneMatch(c -> { if ("*".equals(elem.getQualid().getSimpleName())) { From 7aed57ce398757ad990c5a65c9874f13539829da Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 10 Sep 2024 12:04:18 +0200 Subject: [PATCH 34/82] Apply suggested ChangePluginConfiguration documentation changes Fixes https://github.com/openrewrite/rewrite-docs/pull/300 --- .../org/openrewrite/maven/ChangePluginConfiguration.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/ChangePluginConfiguration.java b/rewrite-maven/src/main/java/org/openrewrite/maven/ChangePluginConfiguration.java index fe7ce651cf3..89c1aac2f84 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/ChangePluginConfiguration.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/ChangePluginConfiguration.java @@ -37,18 +37,20 @@ public class ChangePluginConfiguration extends Recipe { private static final XPathMatcher PLUGINS_MATCHER = new XPathMatcher("/project/build/plugins"); @Option(displayName = "Group", - description = "The first part of a dependency coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION'.", + description = "The first part of the coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION' of the plugin to modify.", example = "org.openrewrite.maven") String groupId; @Option(displayName = "Artifact", - description = "The second part of a dependency coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION'.", + description = "The second part of a coordinate 'org.openrewrite.maven:rewrite-maven-plugin:VERSION' of the plugin to modify.", example = "rewrite-maven-plugin") String artifactId; @Language("xml") @Option(displayName = "Configuration", - description = "Plugin configuration provided as raw XML. Supplying `null` will remove any existing configuration.", + description = "Plugin configuration provided as raw XML overriding any existing configuration. " + + "Configuration inside `` blocks will not be altered. " + + "Supplying `null` will remove any existing configuration.", example = "bar", required = false) @Nullable From 7f14d4a37580372e2ae4cb450363b0edf9ffbd1e Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Tue, 10 Sep 2024 12:04:52 +0200 Subject: [PATCH 35/82] `RemoveUnusedImports` should retain explicit import when there are conflicting classes in the same package (#4482) * Add testcase * Apply feedback * Apply feedback: * fix whitespace * clearer test method name * clearer class name * Apply suggestions from code review --------- Co-authored-by: Tim te Beek --- .../java/RemoveUnusedImportsTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java index cae81b2e3b7..f269a88cc19 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java @@ -1983,4 +1983,56 @@ public void foo() { ) ); } + + @Test + void retainExplicitImportWhenConflictingClassInSamePackage() { + rewriteRun( + java( + """ + package com.a; + + class ConflictingClass { + } + """, + SourceSpec::skip + ), + java( + """ + package com.b; + + public class ConflictingClass { + public ConflictingClass() { + } + } + """, + SourceSpec::skip + ), + java( + """ + package com.c; + + import com.b.ConflictingClass; + + public class ImplProvider { + static ConflictingClass getImpl(){ + return new ConflictingClass(); + } + } + """, + SourceSpec::skip + ), + java( + """ + package com.a; + + import com.b.ConflictingClass; + import com.c.ImplProvider; + + class CImpl { + ConflictingClass impl = ImplProvider.getImpl(); + } + """ + ) + ); + } } From 7beef25ee3dd879792fad74c8dafd22ea2a7e329 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 10 Sep 2024 12:20:14 -0700 Subject: [PATCH 36/82] Polish --- .../openrewrite/java/ReplaceAnnotation.java | 2 +- .../java/ReplaceAnnotationTest.java | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java b/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java index c221bd12fc7..7be95f7d78a 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java @@ -50,7 +50,7 @@ public class ReplaceAnnotation extends Recipe { @Override public String getDisplayName() { - return "Replace Annotation"; + return "Replace annotation"; } @Override diff --git a/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java b/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java index 075c09dc74b..7a63c0fb818 100644 --- a/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java +++ b/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java @@ -25,6 +25,7 @@ class ReplaceAnnotationTest implements RewriteTest { + @SuppressWarnings("NullableProblems") @Nested class OnMatch { @Test @@ -34,7 +35,7 @@ void matchNoPrams() { java( """ import org.jetbrains.annotations.NotNull; - + class A { @NotNull String testMethod() {} @@ -42,7 +43,7 @@ String testMethod() {} """, """ import lombok.NonNull; - + class A { @NonNull String testMethod() {} @@ -60,7 +61,7 @@ void matchWithPrams() { java( """ import org.jetbrains.annotations.NotNull; - + class A { @NotNull("Test") String testMethod() {} @@ -68,7 +69,7 @@ String testMethod() {} """, """ import lombok.NonNull; - + class A { @NonNull String testMethod() {} @@ -85,7 +86,7 @@ void insertWithParams() { java( """ import lombok.NonNull; - + class A { @NonNull String testMethod() {} @@ -93,7 +94,7 @@ String testMethod() {} """, """ import org.jetbrains.annotations.NotNull; - + class A { @NotNull("Test") String testMethod() {} @@ -112,7 +113,7 @@ void methodWithAnnotatedParameter() { """ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; - + class A { void methodName( @Nullable final boolean valueVar) { @@ -123,7 +124,7 @@ void methodName( """ import lombok.NonNull; import org.jetbrains.annotations.Nullable; - + class A { void methodName( @Nullable final boolean valueVar) { @@ -145,7 +146,7 @@ void noMatchOtherType() { java( """ import org.jetbrains.annotations.Nullable; - + class A { @Nullable("Test") String testMethod() {} @@ -162,7 +163,7 @@ void noMatchParameter() { java( """ import org.jetbrains.annotations.Nullable; - + class A { @Nullable("Other") String testMethod() {} From aa3683e01d138034f91c2bd2af920701bdd49480 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 10 Sep 2024 12:20:49 -0700 Subject: [PATCH 37/82] Add builder to GradleProject and related markers to make them more convenient to instantiate in tests and utilities --- rewrite-gradle/build.gradle.kts | 4 +-- .../gradle/marker/GradleBuildscript.java | 25 +++++++++++----- .../gradle/marker/GradleProject.java | 30 ++++++++++++++----- .../gradle/marker/GradleSettings.java | 27 +++++++++++++---- 4 files changed, 63 insertions(+), 23 deletions(-) diff --git a/rewrite-gradle/build.gradle.kts b/rewrite-gradle/build.gradle.kts index 6a13b318e64..9f18146b3b0 100644 --- a/rewrite-gradle/build.gradle.kts +++ b/rewrite-gradle/build.gradle.kts @@ -21,7 +21,7 @@ repositories { } //val rewriteVersion = rewriteRecipe.rewriteVersion.get() -val rewriteVersion = if(project.hasProperty("releasing")) { +val latest = if (project.hasProperty("releasing")) { "latest.release" } else { "latest.integration" @@ -59,7 +59,7 @@ dependencies { exclude("ch.qos.logback", "logback-classic") } - testImplementation("org.openrewrite.gradle.tooling:model:latest.release") + testImplementation("org.openrewrite.gradle.tooling:model:$latest") testImplementation("com.squareup.okhttp3:mockwebserver:4.+") diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java index 7c08a94dfba..81cee15799b 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java @@ -15,23 +15,34 @@ */ package org.openrewrite.gradle.marker; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Value; import lombok.With; import org.jspecify.annotations.Nullable; import org.openrewrite.maven.tree.MavenRepository; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.openrewrite.Tree.randomId; @Value @With +@Builder +@AllArgsConstructor public class GradleBuildscript implements Serializable { - UUID id; - List mavenRepositories; - Map nameToConfiguration; + + @Builder.Default + UUID id = randomId(); + + @Builder.Default + List mavenRepositories = emptyList(); + + @Builder.Default + Map nameToConfiguration = emptyMap(); public @Nullable GradleDependencyConfiguration getConfiguration(String name) { return nameToConfiguration.get(name); diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java index 3aa601e2571..5237e8e135c 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Value; import lombok.With; import org.jspecify.annotations.Nullable; @@ -26,6 +27,10 @@ import java.io.Serializable; import java.util.*; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.openrewrite.Tree.randomId; + /** * Contains metadata about a Gradle Project. Queried from Gradle itself when the OpenRewrite build plugin runs. @@ -35,38 +40,47 @@ @SuppressWarnings("unused") @Value @AllArgsConstructor(onConstructor_ = { @JsonCreator }) +@Builder public class GradleProject implements Marker, Serializable { + @With - UUID id; + @Builder.Default + UUID id = randomId(); @With @Nullable String group; @With - String name; + @Builder.Default + String name = ""; @With @Nullable String version; @With - String path; + @Builder.Default + String path = ""; @With - List plugins; + @Builder.Default + List plugins = emptyList(); @With - List mavenRepositories; + @Builder.Default + List mavenRepositories = emptyList(); @With @Deprecated @Nullable List mavenPluginRepositories; - Map nameToConfiguration; + @Builder.Default + Map nameToConfiguration = emptyMap(); - GradleBuildscript buildscript; + @Builder.Default + GradleBuildscript buildscript = new GradleBuildscript(randomId(), emptyList(), emptyMap()); // Backwards compatibility to ease convoluted release process with rewrite-gradle-tooling-model public GradleProject( @@ -94,7 +108,7 @@ public List getMavenPluginRepositories() { if (buildscript != null) { return buildscript.getMavenRepositories(); } - return mavenPluginRepositories == null ? Collections.emptyList() : mavenPluginRepositories; + return mavenPluginRepositories == null ? emptyList() : mavenPluginRepositories; } public @Nullable GradleDependencyConfiguration getConfiguration(String name) { diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java index 2bd2db8cd63..8c5dfa802b5 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java @@ -17,9 +17,11 @@ import com.fasterxml.jackson.annotation.JsonCreator; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Value; import lombok.With; import org.jspecify.annotations.Nullable; +import org.openrewrite.Tree; import org.openrewrite.marker.Marker; import org.openrewrite.maven.tree.MavenRepository; @@ -27,19 +29,32 @@ import java.util.*; import java.util.stream.Collectors; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.openrewrite.Tree.randomId; + @Value @With @AllArgsConstructor(onConstructor_ = { @JsonCreator}) +@Builder public class GradleSettings implements Marker, Serializable { - UUID id; + + @Builder.Default + UUID id = randomId(); @Deprecated @Nullable - List pluginRepositories; + @Builder.Default + List pluginRepositories = emptyList(); + + @Builder.Default + List plugins = emptyList(); + + @Builder.Default + Map featurePreviews = emptyMap(); - List plugins; - Map featurePreviews; - GradleBuildscript buildscript; + @Builder.Default + GradleBuildscript buildscript = GradleBuildscript.builder().build(); // Backwards compatibility to ease convoluted release process with rewrite-gradle-tooling-model public GradleSettings( @@ -72,6 +87,6 @@ public List getPluginRepositories() { if (buildscript != null) { return buildscript.getMavenRepositories(); } - return pluginRepositories == null ? Collections.emptyList() : pluginRepositories; + return pluginRepositories == null ? emptyList() : pluginRepositories; } } From 4edba6d4fd990722618ab2dcb8e60292bf1f49b7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 11 Sep 2024 13:17:34 +0200 Subject: [PATCH 38/82] Fallback to settings.xml for MavenExecutionContextView.getLocalRepository (#4484) --- .../openrewrite/maven/MavenExecutionContextView.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java index 9bf7cd958ca..93d0109666e 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenExecutionContextView.java @@ -141,7 +141,15 @@ public MavenExecutionContextView setLocalRepository(MavenRepository localReposit } public MavenRepository getLocalRepository() { - return getMessage(MAVEN_LOCAL_REPOSITORY, MAVEN_LOCAL_DEFAULT); + MavenRepository configuredProperty = getMessage(MAVEN_LOCAL_REPOSITORY); + if (configuredProperty != null) { + return configuredProperty; + } + MavenSettings settings = getSettings(); + if (settings != null) { + return settings.getMavenLocal(); + } + return MAVEN_LOCAL_DEFAULT; } public MavenExecutionContextView setAddLocalRepository(boolean useLocalRepository) { From 13cd2e902a9955d41dd83bfdd054ab2cf453c7f1 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 15:13:50 -0700 Subject: [PATCH 39/82] Update dependenciesBlockInFreestandingScript to exercise dynamic dependency resolution --- .../gradle/marker/GradleSettings.java | 1 - .../gradle/UpgradeDependencyVersionTest.java | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java index 8c5dfa802b5..ed3431212c0 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java @@ -21,7 +21,6 @@ import lombok.Value; import lombok.With; import org.jspecify.annotations.Nullable; -import org.openrewrite.Tree; import org.openrewrite.marker.Marker; import org.openrewrite.maven.tree.MavenRepository; diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java index fc236b8d3be..768fe0eef03 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java @@ -578,9 +578,9 @@ void versionInParentSubprojectDefinitionWithPropertiesFiles() { repositories { mavenCentral() } - + apply plugin: "java-library" - + dependencies { implementation ("com.google.guava:guava:$guavaVersion") } @@ -599,7 +599,7 @@ void versionInParentSubprojectDefinitionWithPropertiesFiles() { plugins { id 'java-library' } - + repositories { mavenCentral() } @@ -969,11 +969,11 @@ void upgradesDependencyVersionDefinedInJvmTestSuite() { id "java-library" id 'jvm-test-suite' } - + repositories { mavenCentral() } - + testing { suites { test { @@ -989,11 +989,11 @@ void upgradesDependencyVersionDefinedInJvmTestSuite() { id "java-library" id 'jvm-test-suite' } - + repositories { mavenCentral() } - + testing { suites { test { @@ -1011,7 +1011,7 @@ void upgradesDependencyVersionDefinedInJvmTestSuite() { @Test void dependenciesBlockInFreestandingScript() { rewriteRun( - spec -> spec.recipe(new UpgradeDependencyVersion("com.fasterxml.jackson.core", "jackson-databind", "2.17.2", null)), + spec -> spec.recipe(new UpgradeDependencyVersion("com.fasterxml.jackson.core", "jackson-databind", "2.17.0-2.17.2", null)), buildGradle( """ repositories { From 1e2e9f21117eea94e7152293af1166aa259cfa41 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 16:05:10 -0700 Subject: [PATCH 40/82] Unit test framework now asserts that poms have a MavenResolutionResult --- .../java/org/openrewrite/maven/Assertions.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java index 2a98c3213dd..cca14c30994 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java @@ -18,12 +18,17 @@ import org.intellij.lang.annotations.Language; import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; +import org.openrewrite.maven.tree.MavenResolutionResult; import org.openrewrite.test.SourceSpec; import org.openrewrite.test.SourceSpecs; +import org.openrewrite.test.TypeValidation; import org.openrewrite.xml.tree.Xml; import java.util.function.Consumer; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class Assertions { private Assertions() { } @@ -41,7 +46,7 @@ public static SourceSpecs pomXml(@Language("xml") @Nullable String before) { public static SourceSpecs pomXml(@Language("xml") @Nullable String before, Consumer> spec) { SourceSpec maven = new SourceSpec<>(Xml.Document.class, "maven", MavenParser.builder(), before, - SourceSpec.ValidateSource.noop, Assertions::customizeExecutionContext); + Assertions::pomResolvedSuccessfully, Assertions::customizeExecutionContext); maven.path("pom.xml"); spec.accept(maven); return maven; @@ -55,10 +60,16 @@ public static SourceSpecs pomXml(@Language("xml") @Nullable String before, @Lang public static SourceSpecs pomXml(@Language("xml") @Nullable String before, @Language("xml") @Nullable String after, Consumer> spec) { SourceSpec maven = new SourceSpec<>(Xml.Document.class, "maven", MavenParser.builder(), before, - SourceSpec.ValidateSource.noop, Assertions::customizeExecutionContext).after(s -> after); + Assertions::pomResolvedSuccessfully, Assertions::customizeExecutionContext).after(s -> after); maven.path("pom.xml"); spec.accept(maven); return maven; } + private static SourceFile pomResolvedSuccessfully(SourceFile sourceFile, TypeValidation typeValidation) { + sourceFile.getMarkers() + .findFirst(MavenResolutionResult.class) + .orElseThrow(() -> new IllegalStateException("No MavenResolutionResult found")); + return sourceFile; + } } From 45f51983ca714f505528e81fa5c0f1f2c77402aa Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 16:05:55 -0700 Subject: [PATCH 41/82] Use noop recipe rather than throwing an error when no recipe is configured. --- .../src/main/java/org/openrewrite/test/RewriteTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java b/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java index 2560c78f508..42af9090ac9 100644 --- a/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java +++ b/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java @@ -33,7 +33,6 @@ import org.openrewrite.remote.Remote; import org.openrewrite.tree.ParseError; -import java.io.ByteArrayInputStream; import java.nio.file.Path; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; @@ -152,10 +151,9 @@ default void rewriteRun(Consumer spec, SourceSpec... sourceSpecs) PrintOutputCapture out = new PrintOutputCapture<>(0, markerPrinter); - Recipe recipe = testMethodSpec.recipe == null ? testClassSpec.recipe : testMethodSpec.recipe; - assertThat(recipe) - .as("A recipe must be specified") - .isNotNull(); + Recipe recipe = testMethodSpec.recipe == null ? + testClassSpec.recipe == null ? Recipe.noop() : testClassSpec.recipe : + testMethodSpec.recipe; if (!(recipe instanceof AdHocRecipe) && !(recipe instanceof AdHocScanningRecipe) && !(recipe instanceof CompositeRecipe) && !(recipe.equals(Recipe.noop())) && From 7b2093aaf4c8a946dbf62cba3f42e5690aba05ad Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 16:52:29 -0700 Subject: [PATCH 42/82] Implement support for Maven's prerequisites mechanism --- .../openrewrite/maven/internal/RawPom.java | 12 ++ .../java/org/openrewrite/maven/tree/Pom.java | 3 + .../openrewrite/maven/tree/Prerequisites.java | 32 +++ .../openrewrite/maven/tree/ResolvedPom.java | 4 + .../openrewrite/maven/MavenParserTest.java | 194 +++++++++++------- .../maven/internal/RawPomTest.java | 17 +- 6 files changed, 183 insertions(+), 79 deletions(-) create mode 100644 rewrite-maven/src/main/java/org/openrewrite/maven/tree/Prerequisites.java diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java index 4befdcfd431..2468380c964 100755 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java @@ -90,6 +90,9 @@ public class RawPom { @Nullable String description; + @Nullable + Prerequisites prerequisites; + @Nullable String packaging; @@ -192,6 +195,14 @@ public Licenses(@JacksonXmlProperty(localName = "license") List license } } + @Getter + public static class Prerequisites { + + @JacksonXmlProperty(localName = "maven") + @Nullable + public String maven; + } + @Getter public static class Profiles { private final List profiles; @@ -371,6 +382,7 @@ public Pom toPom(@Nullable Path inputPath, @Nullable MavenRepository repo) { null)) .name(name) .obsoletePomVersion(pomVersion) + .prerequisites(prerequisites == null ? null : new org.openrewrite.maven.tree.Prerequisites(prerequisites.getMaven())) .packaging(packaging) .properties(getProperties() == null ? emptyMap() : getProperties()) .licenses(mapLicenses(getLicenses())) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java index 6bd532cc6c7..e5af8d7bb32 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Pom.java @@ -85,6 +85,9 @@ public static int getModelVersion() { @Nullable String name; + @Nullable + Prerequisites prerequisites; + @Nullable String packaging; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Prerequisites.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Prerequisites.java new file mode 100644 index 00000000000..77e0df37125 --- /dev/null +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Prerequisites.java @@ -0,0 +1,32 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://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. + */ +package org.openrewrite.maven.tree; + +import lombok.Value; +import org.jspecify.annotations.Nullable; + +/** + * Models the prerequisites element of a POM. + */ +@Value +public class Prerequisites { + + /** + * The minimum version of Maven required to build the project. + */ + @Nullable + String maven; +} diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java index 2969265eed7..8d3d08db44c 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/ResolvedPom.java @@ -301,6 +301,10 @@ public String getPackaging() { case "project.parent.version": case "parent.version": return requested.getParent() != null ? requested.getParent().getVersion() : null; + case "prerequisites.maven": + case "pom.prerequisites.maven": + case "project.prerequisites.maven": + return requested.getPrerequisites() == null ? null : requested.getPrerequisites().getMaven(); } return System.getProperty(property); diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java index 44e1d67106c..895917dd922 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java @@ -55,7 +55,7 @@ void rangeVersion() { com.mycompany.app my-app 1 - + junit @@ -69,6 +69,55 @@ void rangeVersion() { ); } + @Test + void prerequisites() { + rewriteRun( + //language=xml + pomXml( + """ + + org.sample + sample + 1.0.0 + + 3.0 + + + + org.apache.maven.reporting + maven-reporting-api + ${project.prerequisites.maven} + + + + """ + ) + ); + } + + @Test + void jacoco() { + rewriteRun( + //language=xml + pomXml( + """ + + org.sample + sample + 1.0.0 + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + """ + ) + ); + } + @Test void skipDependencyResolution() { rewriteRun( @@ -79,7 +128,7 @@ void skipDependencyResolution() { com.mycompany.app my-app 1 - + foo @@ -128,7 +177,7 @@ void invalidRange() { com.mycompany.app my-app 1 - + junit @@ -517,7 +566,7 @@ void parse() { """ 4.0.0 - + com.mycompany.app my-app 1 @@ -527,7 +576,7 @@ void parse() { Trygve Laugstøl - + org.junit.jupiter @@ -583,11 +632,11 @@ void handlesRepositories() { """ 4.0.0 - + org.openrewrite.maven single-project 0.1.0-SNAPSHOT - + com.google.guava @@ -595,7 +644,7 @@ void handlesRepositories() { 29.0-jre - + jcenter @@ -617,15 +666,15 @@ void handlesPropertiesInDependencyScope() { """ 4.0.0 - + org.openrewrite.maven single-project 0.1.0-SNAPSHOT - + compile - + com.google.guava @@ -677,11 +726,11 @@ void selfRecursiveParent() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.mycompany.app my-app @@ -701,11 +750,11 @@ void selfRecursiveDependency() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.mycompany.app @@ -893,11 +942,11 @@ public MockResponse dispatch(RecordedRequest request) { resp.setBody(""" 4.0.0 - + com.foo bar 1.0.0 - + """ ); @@ -940,11 +989,11 @@ public MockResponse dispatch(RecordedRequest request) { """ 4.0.0 - + org.openrewrite.test foo 0.1.0-SNAPSHOT - + com.foo @@ -1130,17 +1179,17 @@ void indirectBomImportedFromParent() { """ 4.0.0 - + b org.openrewrite.maven 0.1.0-SNAPSHOT pom - + 1.8 1.8 - + @@ -1161,12 +1210,12 @@ void indirectBomImportedFromParent() { """ 4.0.0 - + c org.openrewrite.maven 0.1.0-SNAPSHOT pom - + @@ -1185,11 +1234,11 @@ void indirectBomImportedFromParent() { """ 4.0.0 - + org.openrewrite.maven d 0.1.0-SNAPSHOT - + 1.8 1.8 @@ -1283,6 +1332,7 @@ void parseEmptyValueActivationTag() { assertThat(activation).isNotNull(); assertThat(activation.getActiveByDefault()).isNull(); assertThat(activation.getJdk()).isNull(); + //noinspection DataFlowIssue assertThat(activation.getProperty()).isNull(); }) ) @@ -1406,7 +1456,7 @@ void dependencyManagementPropagatesToDependencies() { a-parent 0.1.0-SNAPSHOT pom - + @@ -1429,9 +1479,9 @@ void dependencyManagementPropagatesToDependencies() { 0.1.0-SNAPSHOT - + a - + org.openrewrite.maven @@ -1490,9 +1540,9 @@ void dependencyManagementPropagatesToDependencies() { 0.1.0-SNAPSHOT - + b - + org.openrewrite.maven @@ -1606,7 +1656,7 @@ void profileNoJdkActivation() { com.mycompany.app my-app 1 - + old-jdk @@ -1812,11 +1862,11 @@ void ciFriendlyVersionWithParent() { sample ${revision} pom - + sample-rest - + """, spec -> spec.path("pom.xml")), pomXml( @@ -1824,11 +1874,11 @@ void ciFriendlyVersionWithParent() { - + 4.0.0 sample-rest jar - + net.sample sample @@ -1854,14 +1904,14 @@ void canConnectProjectPomsWhenUsingCiFriendlyVersions() { sample ${revision} pom - + sample-parent sample-app sample-rest sample-web - + 0.0.0-SNAPSHOT @@ -1877,11 +1927,11 @@ void canConnectProjectPomsWhenUsingCiFriendlyVersions() { net.sample ${revision} pom - + 0.0.0-SNAPSHOT - + @@ -1903,24 +1953,24 @@ void canConnectProjectPomsWhenUsingCiFriendlyVersions() { - + 4.0.0 sample-app jar - + net.sample sample-parent ${revision} ../parent/pom.xml - + net.sample sample-rest - + net.sample sample-web @@ -1933,11 +1983,11 @@ void canConnectProjectPomsWhenUsingCiFriendlyVersions() { - + 4.0.0 sample-rest jar - + net.sample sample-parent @@ -1951,11 +2001,11 @@ void canConnectProjectPomsWhenUsingCiFriendlyVersions() { - + 4.0.0 sample-web jar - + net.sample sample-parent @@ -1982,14 +2032,14 @@ void ciFriendlyVersionsStillWorkAfterUpdateMavenModel() { sample ${revision} pom - + sample-parent sample-app sample-rest sample-web - + 0.0.0-SNAPSHOT @@ -2005,11 +2055,11 @@ void ciFriendlyVersionsStillWorkAfterUpdateMavenModel() { net.sample ${revision} pom - + 0.0.0-SNAPSHOT - + @@ -2031,29 +2081,29 @@ void ciFriendlyVersionsStillWorkAfterUpdateMavenModel() { - + 4.0.0 sample-app jar - + net.sample sample-parent ${revision} ../parent/pom.xml - + net.sample sample-rest - + net.sample sample-web - + junit junit @@ -2065,29 +2115,29 @@ void ciFriendlyVersionsStillWorkAfterUpdateMavenModel() { - + 4.0.0 sample-app jar - + net.sample sample-parent ${revision} ../parent/pom.xml - + net.sample sample-rest - + net.sample sample-web - + junit junit @@ -2101,11 +2151,11 @@ void ciFriendlyVersionsStillWorkAfterUpdateMavenModel() { - + 4.0.0 sample-rest jar - + net.sample sample-parent @@ -2119,11 +2169,11 @@ void ciFriendlyVersionsStillWorkAfterUpdateMavenModel() { - + 4.0.0 sample-web jar - + net.sample sample-parent @@ -2144,16 +2194,16 @@ void multipleCiFriendlyVersionPlaceholders() { 4.0.0 - + bogus.example parent ${revision}${changelist} pom - + sub - + 99999.0 -SNAPSHOT @@ -2166,13 +2216,13 @@ void multipleCiFriendlyVersionPlaceholders() { 4.0.0 - + bogus.example parent ${revision}${changelist} - + sub """, spec -> spec.path("sub/pom.xml")) @@ -2337,7 +2387,7 @@ void malformedPom() { com.mycompany.app my-app 1 - + junit diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/RawPomTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/RawPomTest.java index 4eecefea9f0..8f69e5e1bc2 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/RawPomTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/RawPomTest.java @@ -55,14 +55,15 @@ void profileActivationByAbsenceOfProperty() { @Test void repositoriesSerializationAndDeserialization() { RawPom pom = RawPom.parse( + //language=xml new ByteArrayInputStream(""" `4.0.0 - + com.mycompany.app my-app 1 - + spring-milestones @@ -75,6 +76,7 @@ void repositoriesSerializationAndDeserialization() { null ); + //noinspection DataFlowIssue assertThat(pom.getRepositories()).isNotNull(); assertThat(pom.getRepositories().getRepositories()).hasSize(1); } @@ -82,10 +84,11 @@ void repositoriesSerializationAndDeserialization() { @Test void serializePluginFlags() { RawPom pom = RawPom.parse( + //language=xml new ByteArrayInputStream(""" 4.0.0 - + com.mycompany.app my-app 1 @@ -280,7 +283,7 @@ void deserializePom() throws JsonProcessingException { A business-friendly OSS license - + @@ -299,7 +302,7 @@ void deserializePom() throws JsonProcessingException { default - + java9+ @@ -494,12 +497,12 @@ void deserializePluginConfiguration() throws JsonProcessingException { @Language("xml") String pomString = """ 4.0.0 - + com.mycompany.app my-app 1 jar - + From d2d6cc57822aaf785d4426bc2980d41375ba9758 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 16:55:01 -0700 Subject: [PATCH 43/82] Remove jacoco test which is made redundant by the "prerequisites" test case --- .../openrewrite/maven/MavenParserTest.java | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java index 895917dd922..72de19ebbb5 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java @@ -95,29 +95,6 @@ void prerequisites() { ); } - @Test - void jacoco() { - rewriteRun( - //language=xml - pomXml( - """ - - org.sample - sample - 1.0.0 - - - org.jacoco - jacoco-maven-plugin - 0.8.12 - - - - """ - ) - ); - } - @Test void skipDependencyResolution() { rewriteRun( @@ -1277,6 +1254,7 @@ void parseNotInProfileActivation() { @Issue("https://github.com/openrewrite/rewrite/issues/1427") @Test void parseEmptyActivationTag() { + //noinspection DataFlowIssue rewriteRun( pomXml( """ From 003325ed94214ec086cffe99c1c6591ee4eb992d Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 17:44:52 -0700 Subject: [PATCH 44/82] Fix test that fails due to MavenResolutionResult validation --- .../org/openrewrite/maven/Assertions.java | 10 ++--- .../maven/MavenDependencyFailuresTest.java | 30 ++++++++------ .../org/openrewrite/test/TypeValidation.java | 41 ++++++++++++++++++- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java index cca14c30994..a6a38e4e68f 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java @@ -27,8 +27,6 @@ import java.util.function.Consumer; -import static org.junit.jupiter.api.Assertions.assertEquals; - public class Assertions { private Assertions() { } @@ -67,9 +65,11 @@ public static SourceSpecs pomXml(@Language("xml") @Nullable String before, @Lang } private static SourceFile pomResolvedSuccessfully(SourceFile sourceFile, TypeValidation typeValidation) { - sourceFile.getMarkers() - .findFirst(MavenResolutionResult.class) - .orElseThrow(() -> new IllegalStateException("No MavenResolutionResult found")); + if (typeValidation.dependencyModel()) { + sourceFile.getMarkers() + .findFirst(MavenResolutionResult.class) + .orElseThrow(() -> new IllegalStateException("No MavenResolutionResult found")); + } return sourceFile; } } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenDependencyFailuresTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenDependencyFailuresTest.java index 5e284f274ec..1a179b991a4 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenDependencyFailuresTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenDependencyFailuresTest.java @@ -25,6 +25,7 @@ import org.openrewrite.maven.tree.MavenResolutionResult; import org.openrewrite.maven.tree.Scope; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; import org.openrewrite.xml.tree.Xml; import java.io.IOException; @@ -222,7 +223,11 @@ void unresolvableTransitiveDependencyDueToInvalidPom(@TempDir Path localReposito @Test void unresolvableDependency() { rewriteRun( - spec -> spec.executionContext(new InMemoryExecutionContext()), + spec -> spec.executionContext(new InMemoryExecutionContext()) + .typeValidationOptions(TypeValidation.builder() + // Skip test framework validation that a MavenResolutionResult is present since this tests exercises error handling + .dependencyModel(false) + .build()), pomXml( """ @@ -247,7 +252,8 @@ void unresolvableDependency() { """, - spec -> spec.afterRecipe(after -> { + spec -> spec + .afterRecipe(after -> { Optional maybeParseException = after.getMarkers().findFirst(ParseExceptionResult.class); assertThat(maybeParseException).hasValueSatisfying(per -> assertThat(per.getMessage()).contains("Unable to download POM: com.google.guava:guava:doesnotexist. Tried repositories")); }) @@ -326,17 +332,15 @@ void unresolvableTransitiveDependencyDueToInvalidPom() { """, spec -> spec.afterRecipe(pom -> - { - assertThat(pom.getMarkers().findFirst(MavenResolutionResult.class)) - .isPresent() - .get() - .extracting(mrr -> mrr.getDependencies().get(Scope.Compile)) - .matches(deps -> deps.size() == 1) - .extracting(deps -> deps.get(0)) - .matches(dep -> dep.getGroupId().equals("org.jvnet.staxex") && - dep.getArtifactId().equals("stax-ex") && - dep.getVersion().equals("1.0")); - })) + assertThat(pom.getMarkers().findFirst(MavenResolutionResult.class)) + .isPresent() + .get() + .extracting(mrr -> mrr.getDependencies().get(Scope.Compile)) + .matches(deps -> deps.size() == 1) + .extracting(deps -> deps.get(0)) + .matches(dep -> dep.getGroupId().equals("org.jvnet.staxex") && + dep.getArtifactId().equals("stax-ex") && + dep.getVersion().equals("1.0")))) ); } diff --git a/rewrite-test/src/main/java/org/openrewrite/test/TypeValidation.java b/rewrite-test/src/main/java/org/openrewrite/test/TypeValidation.java index 4c51773c841..6afde8de32e 100644 --- a/rewrite-test/src/main/java/org/openrewrite/test/TypeValidation.java +++ b/rewrite-test/src/main/java/org/openrewrite/test/TypeValidation.java @@ -21,6 +21,12 @@ import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +/** + * Controls the test framework's validation of invariants which are expected to hold true in an LST both before and + * after the recipe run. Originally this applied only to validating the well-formedness of type metadata in Java LSTs + * (hence the name TypeValidation), but has since expanded to include concepts relevant to other types of sources. + * "InvariantValidation" would be a more accurate name, but "TypeValidation" is kept for backwards compatibility. + */ @Data @NoArgsConstructor @AllArgsConstructor @@ -28,30 +34,63 @@ @Builder public class TypeValidation { + /** + * Controls whether class declarations are validated to have valid type metadata. + */ @Builder.Default private boolean classDeclarations = true; + /** + * Controls whether identifiers declarations are validated to have valid type metadata. + * Within even a well-formed Java LST not all identifiers typically have type metadata, so even when enabled some + * identifiers are allowed to have null type. + */ @Builder.Default private boolean identifiers = true; + /** + * Controls whether method declarations are validated to have valid type metadata. + */ @Builder.Default private boolean methodDeclarations = true; + /** + * Controls whether field declarations are validated to have valid type metadata. + */ @Builder.Default private boolean variableDeclarations = true; + /** + * Controls whether method invocations are validated to have valid type metadata. + */ @Builder.Default private boolean methodInvocations = true; + /** + * Controls whether constructor invocations are validated to have valid type metadata. + */ @Builder.Default private boolean constructorInvocations = true; + /** + * Controls whether sources expected to have dependency resolution metadata in a model marker are validated to have + * that model attached. For example, a Maven pom is expected to have a MavenResolutionResult model attached. + */ + @Builder.Default + private boolean dependencyModel = true; + + /** + * Enable all invariant validation checks. + */ public static TypeValidation all() { return new TypeValidation(); } + /** + * Skip all invariant validation checks. + */ public static TypeValidation none() { - return new TypeValidation(false,false,false,false,false,false); + return new TypeValidation(false, false, false, false, false, false, false); } static TypeValidation before(RecipeSpec testMethodSpec, RecipeSpec testClassSpec) { From 50cb39c5da3362d1eed4d6ec4562177f007becb0 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 11 Sep 2024 20:07:47 -0700 Subject: [PATCH 45/82] Improve error message thrown when test harness detects missing mavenResolutionResult --- .../java/org/openrewrite/maven/Assertions.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java index a6a38e4e68f..743c69ab2a1 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/Assertions.java @@ -18,20 +18,25 @@ import org.intellij.lang.annotations.Language; import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; +import org.openrewrite.ParseExceptionResult; import org.openrewrite.SourceFile; import org.openrewrite.maven.tree.MavenResolutionResult; import org.openrewrite.test.SourceSpec; import org.openrewrite.test.SourceSpecs; import org.openrewrite.test.TypeValidation; import org.openrewrite.xml.tree.Xml; +import org.opentest4j.AssertionFailedError; import java.util.function.Consumer; +import static org.junit.jupiter.api.Assertions.fail; + public class Assertions { private Assertions() { } + static void customizeExecutionContext(ExecutionContext ctx) { - if(MavenSettings.readFromDiskEnabled()) { + if (MavenSettings.readFromDiskEnabled()) { MavenExecutionContextView mctx = MavenExecutionContextView.view(ctx); mctx.setMavenSettings(MavenSettings.readMavenSettingsFromDisk(mctx)); } @@ -56,9 +61,9 @@ public static SourceSpecs pomXml(@Language("xml") @Nullable String before, @Lang } public static SourceSpecs pomXml(@Language("xml") @Nullable String before, @Language("xml") @Nullable String after, - Consumer> spec) { + Consumer> spec) { SourceSpec maven = new SourceSpec<>(Xml.Document.class, "maven", MavenParser.builder(), before, - Assertions::pomResolvedSuccessfully, Assertions::customizeExecutionContext).after(s -> after); + Assertions::pomResolvedSuccessfully, Assertions::customizeExecutionContext).after(s -> after); maven.path("pom.xml"); spec.accept(maven); return maven; @@ -66,9 +71,12 @@ public static SourceSpecs pomXml(@Language("xml") @Nullable String before, @Lang private static SourceFile pomResolvedSuccessfully(SourceFile sourceFile, TypeValidation typeValidation) { if (typeValidation.dependencyModel()) { + sourceFile.getMarkers() + .findFirst(ParseExceptionResult.class) + .ifPresent(parseExceptionResult -> fail("Problem parsing " + sourceFile.getSourcePath() + ":\n" + parseExceptionResult.getMessage())); sourceFile.getMarkers() .findFirst(MavenResolutionResult.class) - .orElseThrow(() -> new IllegalStateException("No MavenResolutionResult found")); + .orElseThrow(() -> new AssertionFailedError("No MavenResolutionResult found on " + sourceFile.getSourcePath())); } return sourceFile; } From 3e694bb36b575087592ab5ee42df79be03df9187 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 12 Sep 2024 14:05:25 +0200 Subject: [PATCH 46/82] Also show details for singular MavenDownloadingException (#4486) --- .../java/org/openrewrite/maven/MavenParser.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenParser.java b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenParser.java index 89f18618ffd..c37f373f0c9 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenParser.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenParser.java @@ -77,7 +77,7 @@ public Stream parseInputs(Iterable sources, @Nullable Path re Pom pom = RawPom.parse(source.getSource(ctx), null) .toPom(pomPath, null); - if (pom.getProperties() == null || pom.getProperties().isEmpty()) { + if (pom.getProperties().isEmpty()) { pom = pom.withProperties(new LinkedHashMap<>()); } String baseDir = pomPath.toAbsolutePath().getParent().toString(); @@ -117,15 +117,14 @@ public Stream parseInputs(Iterable sources, @Nullable Path re } parsed.add(docToPom.getKey().withMarkers(docToPom.getKey().getMarkers().compute(model, (old, n) -> n))); } catch (MavenDownloadingExceptions e) { - ParseExceptionResult parseExceptionResult = new ParseExceptionResult( - randomId(), - MavenParser.class.getSimpleName(), - e.getClass().getSimpleName(), - e.warn(docToPom.getKey()).printAll(), // Shows any underlying MavenDownloadingException - null); - parsed.add(docToPom.getKey().withMarkers(docToPom.getKey().getMarkers().add(parseExceptionResult))); + String message = e.warn(docToPom.getKey()).printAll(); // Shows any underlying MavenDownloadingException + parsed.add(docToPom.getKey().withMarkers(docToPom.getKey().getMarkers().add(ParseExceptionResult.build(this, e, message)))); ctx.getOnError().accept(e); - } catch (MavenDownloadingException | UncheckedIOException e) { + } catch (MavenDownloadingException e) { + String message = e.warn(docToPom.getKey()).printAll(); // Shows any underlying MavenDownloadingException + parsed.add(docToPom.getKey().withMarkers(docToPom.getKey().getMarkers().add(ParseExceptionResult.build(this, e, message)))); + ctx.getOnError().accept(e); + } catch (UncheckedIOException e) { parsed.add(docToPom.getKey().withMarkers(docToPom.getKey().getMarkers().add(ParseExceptionResult.build(this, e)))); ctx.getOnError().accept(e); } From 3be53f3ef71d2b804ce8125582fc7e990c580d08 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Thu, 12 Sep 2024 11:38:53 -0700 Subject: [PATCH 47/82] Allow exact version upgrades to Maven versions that exist but are missing in maven metadata --- .../org/openrewrite/semver/ExactVersion.java | 2 + .../maven/UpgradeDependencyVersion.java | 35 ++++- .../maven/UpgradeDependencyVersionTest.java | 132 +++++++++++++----- 3 files changed, 128 insertions(+), 41 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/ExactVersion.java b/rewrite-core/src/main/java/org/openrewrite/semver/ExactVersion.java index d88a3cf2644..54098d38546 100755 --- a/rewrite-core/src/main/java/org/openrewrite/semver/ExactVersion.java +++ b/rewrite-core/src/main/java/org/openrewrite/semver/ExactVersion.java @@ -15,6 +15,7 @@ */ package org.openrewrite.semver; +import lombok.Getter; import org.jspecify.annotations.Nullable; import org.openrewrite.Validated; @@ -22,6 +23,7 @@ * Version selector for matching exact version: either explicitly prefixed with "=", * or implicit default when no other version selectors match. */ +@Getter public class ExactVersion extends LatestRelease { String version; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java index 7d739a8e837..fe60784197a 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java @@ -19,9 +19,11 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.maven.internal.MavenPomDownloader; import org.openrewrite.maven.table.MavenMetadataFailures; import org.openrewrite.maven.tree.*; import org.openrewrite.maven.utilities.RetainVersions; +import org.openrewrite.semver.ExactVersion; import org.openrewrite.semver.LatestPatch; import org.openrewrite.semver.Semver; import org.openrewrite.semver.VersionComparator; @@ -34,6 +36,7 @@ import java.util.*; import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; import static java.util.Objects.requireNonNull; import static org.openrewrite.internal.StringUtils.matchesGlob; @@ -151,7 +154,7 @@ public Xml.Tag visitTag(final Xml.Tag tag, final ExecutionContext ctx) { // if the resolved dependency exists AND it does not represent an artifact that was parsed // as a source file, attempt to find a new version. try { - String newerVersion = findNewerVersion(d.getVersion(), + String newerVersion = findNewerVersion(d.getVersion(), getResolutionResult(), () -> downloadMetadata(d.getGroupId(), d.getArtifactId(), ctx), versionComparator, ctx); if (newerVersion != null) { Optional version = tag.getChild("version"); @@ -400,13 +403,16 @@ private String resolveVersion(String version) { private @Nullable String findNewerVersion(String groupId, String artifactId, String version, ExecutionContext ctx) throws MavenDownloadingException { return UpgradeDependencyVersion.this.findNewerVersion( - version, () -> downloadMetadata(groupId, artifactId, ctx), versionComparator, ctx); + version, getResolutionResult(), () -> downloadMetadata(groupId, artifactId, ctx), versionComparator, ctx); } }; } private @Nullable String findNewerVersion( - String version, MavenMetadataFailures.MavenMetadataDownloader download, VersionComparator versionComparator, ExecutionContext ctx) throws MavenDownloadingException { + String version, + MavenResolutionResult mrr, + MavenMetadataFailures.MavenMetadataDownloader download, + VersionComparator versionComparator, ExecutionContext ctx) throws MavenDownloadingException { String finalVersion = !Semver.isVersion(version) ? "0.0.0" : version; // in the case of "latest.patch", a new version can only be derived if the @@ -423,6 +429,29 @@ private String resolveVersion(String version) { versions.add(v); } } + + // Some repositories will have corrupt or incomplete maven metadata which + // prevents an upgrade even to a fixed version. For example this metadata file is missing all versions after 2019: + // https://repository.mapr.com/nexus/content/groups/mapr-public/org/apache/hbase/hbase-annotations/maven-metadata.xml + if (versionComparator instanceof ExactVersion) { + String exactVersion = ((ExactVersion) versionComparator).getVersion(); + if (!versions.contains(exactVersion)) { + try { + // This is a best effort attempt to see if the pom is there anyway, in spite of the + // fact that it's not in the metadata. Usually it won't be, only in situations like the + // MapR repository mentioned in the comment above will it be. + Pom pom = new MavenPomDownloader(emptyMap(), ctx, + mrr.getMavenSettings(), mrr.getActiveProfiles()).download(new GroupArtifactVersion(groupId, artifactId, ((ExactVersion) versionComparator).getVersion()), + null, null, mrr.getPom().getRepositories()); + if (pom.getGav().getVersion().equals(exactVersion)) { + return exactVersion; + } + } catch (MavenDownloadingException e) { + return null; + } + } + } + // handle upgrades from non semver versions like "org.springframework.cloud:spring-cloud-dependencies:Camden.SR5" if (!Semver.isVersion(finalVersion) && !versions.isEmpty()) { versions.sort(versionComparator); diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java index fdb24cd8ccd..19ef04d26e0 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java @@ -577,11 +577,11 @@ void upgradePluginDependenciesOnProperty() { com.mycompany.app my-app 1 - + 4.33.0 - + @@ -605,11 +605,11 @@ void upgradePluginDependenciesOnProperty() { com.mycompany.app my-app 1 - + 4.33.2 - + @@ -1365,11 +1365,11 @@ void removesRedundantExplicitVersionsMatchingOldImport() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -1381,7 +1381,7 @@ void removesRedundantExplicitVersionsMatchingOldImport() { - + org.junit.jupiter @@ -1394,11 +1394,11 @@ void removesRedundantExplicitVersionsMatchingOldImport() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -1410,7 +1410,7 @@ void removesRedundantExplicitVersionsMatchingOldImport() { - + org.junit.jupiter @@ -1431,11 +1431,11 @@ void removesRedundantExplicitVersionsMatchingNewImport() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -1447,7 +1447,7 @@ void removesRedundantExplicitVersionsMatchingNewImport() { - + org.junit.jupiter @@ -1460,11 +1460,11 @@ void removesRedundantExplicitVersionsMatchingNewImport() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -1476,7 +1476,7 @@ void removesRedundantExplicitVersionsMatchingNewImport() { - + org.junit.jupiter @@ -1497,11 +1497,11 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -1513,7 +1513,7 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { - + org.junit.jupiter @@ -1526,11 +1526,11 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -1542,7 +1542,7 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { - + org.junit.jupiter @@ -1571,7 +1571,7 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - + @@ -1583,7 +1583,7 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { - + com.jcraft @@ -1600,7 +1600,7 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - + @@ -1612,7 +1612,7 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { - + com.jcraft @@ -1637,7 +1637,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - + @@ -1649,7 +1649,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { - + com.jcraft @@ -1665,7 +1665,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - + @@ -1677,7 +1677,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { - + com.jcraft @@ -1702,7 +1702,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { org.sample sample 1.0.0 - + @@ -1714,7 +1714,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { - + com.jcraft @@ -1730,7 +1730,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { org.sample sample 1.0.0 - + @@ -1742,7 +1742,7 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { - + com.jcraft @@ -1770,7 +1770,7 @@ void multipleRetainVersions() { org.sample sample 1.0.0 - + @@ -1782,7 +1782,7 @@ void multipleRetainVersions() { - + com.jcraft @@ -1802,7 +1802,7 @@ void multipleRetainVersions() { org.sample sample 1.0.0 - + @@ -1814,7 +1814,7 @@ void multipleRetainVersions() { - + com.jcraft @@ -1871,4 +1871,60 @@ void exactVersionWithPattern() { ) ); } + + @Test + void exactVersionMissingInMavenMetadata() { + rewriteRun( + spec -> spec.recipe(new UpgradeDependencyVersion("org.apache.hbase", "hbase-annotations", "1.4.14.1-mapr-640", + null, null, null)), + pomXml( + """ + + com.mycompany.app + my-app + 1 + + + hbase + https://repository.mapr.com/nexus/content/groups/mapr-public + + false + + + + + + org.apache.hbase + hbase-annotations + 1.1.1-mapr-1602 + + + + """, + """ + + com.mycompany.app + my-app + 1 + + + hbase + https://repository.mapr.com/nexus/content/groups/mapr-public + + false + + + + + + org.apache.hbase + hbase-annotations + 1.4.14.1-mapr-640 + + + + """ + ) + ); + } } From 0b6914b82be589bb6924f273c7ee2cb6c873b853 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Fri, 13 Sep 2024 01:17:13 -0700 Subject: [PATCH 48/82] Enhance AddManagedDependency to better support "latest.patch" version selector. Share logic for looking up new dependency versions between UpgradeDependencyVersion and AddManagedDependency. --- .../java/org/openrewrite/semver/Semver.java | 6 +- .../maven/AddManagedDependency.java | 28 +++--- .../maven/UpgradeDependencyVersion.java | 69 ++------------- .../maven/trait/MavenDependency.java | 85 ++++++++++++++++++- .../maven/AddManagedDependencyTest.java | 48 +++++++++++ 5 files changed, 152 insertions(+), 84 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java b/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java index 37944d04dcb..155e3107f6f 100644 --- a/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java +++ b/rewrite-core/src/main/java/org/openrewrite/semver/Semver.java @@ -26,7 +26,11 @@ public class Semver { private Semver() { } - public static boolean isVersion(String version) { + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isVersion(@Nullable String version) { + if (version == null) { + return false; + } return LatestRelease.RELEASE_PATTERN.matcher(version).matches(); } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java index 8b1db2ef5fd..f07b1f64752 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java @@ -22,10 +22,8 @@ import org.openrewrite.internal.StringUtils; import org.openrewrite.marker.SearchResult; import org.openrewrite.maven.table.MavenMetadataFailures; -import org.openrewrite.maven.tree.MavenMetadata; -import org.openrewrite.maven.tree.MavenResolutionResult; -import org.openrewrite.maven.tree.ResolvedDependency; -import org.openrewrite.maven.tree.ResolvedPom; +import org.openrewrite.maven.trait.MavenDependency; +import org.openrewrite.maven.tree.*; import org.openrewrite.semver.LatestRelease; import org.openrewrite.semver.Semver; import org.openrewrite.semver.VersionComparator; @@ -204,12 +202,20 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) { if (convertedVersion == null) { return maven; } + String convertedGroup = requireNonNull(pom.getValue(groupId)); + String convertedArtifact = requireNonNull(pom.getValue(artifactId)); Validated versionValidation = Semver.validate(convertedVersion, versionPattern); if (versionValidation.isValid()) { VersionComparator versionComparator = requireNonNull(versionValidation.getValue()); try { - String versionToUse = findVersionToUse(versionComparator, pom, ctx); - if (!Objects.equals(versionToUse, pom.getValue(existingManagedDependencyVersion()))) { + // The version of the dependency currently in use (if any) might influence the version comparator + // For example, "latest.patch" gives very different results depending on the version in use + String currentVersion = getResolutionResult().findDependencies(convertedGroup, convertedArtifact, Scope.fromName(scope)).stream() + .map(ResolvedDependency::getVersion) + .findFirst() + .orElse(existingManagedDependencyVersion()); + String versionToUse = MavenDependency.findNewerVersion(convertedGroup, convertedArtifact, currentVersion, getResolutionResult(), metadataFailures, versionComparator, ctx); + if (versionToUse != null && !versionToUse.equals(pom.getValue(existingManagedDependencyVersion()))) { if (ResolvedPom.placeholderHelper.hasPlaceholders(version) && Objects.equals(convertedVersion, versionToUse)) { // revert back to the original version if the version has a placeholder versionToUse = version; @@ -242,16 +248,6 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) { .filter(Objects::nonNull) .findFirst().orElse(null); } - - private @Nullable String findVersionToUse(VersionComparator versionComparator, ResolvedPom containingPom, ExecutionContext ctx) throws MavenDownloadingException { - MavenMetadata mavenMetadata = metadataFailures.insertRows(ctx, () -> downloadMetadata(groupId, artifactId, containingPom, ctx)); - LatestRelease latest = new LatestRelease(versionPattern); - return mavenMetadata.getVersioning().getVersions().stream() - .filter(v -> versionComparator.isValid(null, v)) - .filter(v -> !Boolean.TRUE.equals(releasesOnly) || latest.isValid(null, v)) - .max((v1, v2) -> versionComparator.compare(null, v1, v2)) - .orElse(null); - } }); } } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java index fe60784197a..574a1422661 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java @@ -19,12 +19,10 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.maven.internal.MavenPomDownloader; import org.openrewrite.maven.table.MavenMetadataFailures; +import org.openrewrite.maven.trait.MavenDependency; import org.openrewrite.maven.tree.*; import org.openrewrite.maven.utilities.RetainVersions; -import org.openrewrite.semver.ExactVersion; -import org.openrewrite.semver.LatestPatch; import org.openrewrite.semver.Semver; import org.openrewrite.semver.VersionComparator; import org.openrewrite.xml.AddToTagVisitor; @@ -36,7 +34,6 @@ import java.util.*; import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; import static java.util.Objects.requireNonNull; import static org.openrewrite.internal.StringUtils.matchesGlob; @@ -154,8 +151,8 @@ public Xml.Tag visitTag(final Xml.Tag tag, final ExecutionContext ctx) { // if the resolved dependency exists AND it does not represent an artifact that was parsed // as a source file, attempt to find a new version. try { - String newerVersion = findNewerVersion(d.getVersion(), getResolutionResult(), - () -> downloadMetadata(d.getGroupId(), d.getArtifactId(), ctx), versionComparator, ctx); + String newerVersion = MavenDependency.findNewerVersion(d.getGroupId(), d.getArtifactId(), d.getVersion(), getResolutionResult(), metadataFailures, + versionComparator, ctx); if (newerVersion != null) { Optional version = tag.getChild("version"); if (version.isPresent()) { @@ -402,68 +399,12 @@ private String resolveVersion(String version) { private @Nullable String findNewerVersion(String groupId, String artifactId, String version, ExecutionContext ctx) throws MavenDownloadingException { - return UpgradeDependencyVersion.this.findNewerVersion( - version, getResolutionResult(), () -> downloadMetadata(groupId, artifactId, ctx), versionComparator, ctx); + return MavenDependency.findNewerVersion(groupId, artifactId, version, getResolutionResult(), metadataFailures, + versionComparator, ctx); } }; } - private @Nullable String findNewerVersion( - String version, - MavenResolutionResult mrr, - MavenMetadataFailures.MavenMetadataDownloader download, - VersionComparator versionComparator, ExecutionContext ctx) throws MavenDownloadingException { - String finalVersion = !Semver.isVersion(version) ? "0.0.0" : version; - - // in the case of "latest.patch", a new version can only be derived if the - // current version is a semantic version - if (versionComparator instanceof LatestPatch && !versionComparator.isValid(finalVersion, finalVersion)) { - return null; - } - - try { - MavenMetadata mavenMetadata = metadataFailures.insertRows(ctx, download); - List versions = new ArrayList<>(); - for (String v : mavenMetadata.getVersioning().getVersions()) { - if (versionComparator.isValid(finalVersion, v)) { - versions.add(v); - } - } - - // Some repositories will have corrupt or incomplete maven metadata which - // prevents an upgrade even to a fixed version. For example this metadata file is missing all versions after 2019: - // https://repository.mapr.com/nexus/content/groups/mapr-public/org/apache/hbase/hbase-annotations/maven-metadata.xml - if (versionComparator instanceof ExactVersion) { - String exactVersion = ((ExactVersion) versionComparator).getVersion(); - if (!versions.contains(exactVersion)) { - try { - // This is a best effort attempt to see if the pom is there anyway, in spite of the - // fact that it's not in the metadata. Usually it won't be, only in situations like the - // MapR repository mentioned in the comment above will it be. - Pom pom = new MavenPomDownloader(emptyMap(), ctx, - mrr.getMavenSettings(), mrr.getActiveProfiles()).download(new GroupArtifactVersion(groupId, artifactId, ((ExactVersion) versionComparator).getVersion()), - null, null, mrr.getPom().getRepositories()); - if (pom.getGav().getVersion().equals(exactVersion)) { - return exactVersion; - } - } catch (MavenDownloadingException e) { - return null; - } - } - } - - // handle upgrades from non semver versions like "org.springframework.cloud:spring-cloud-dependencies:Camden.SR5" - if (!Semver.isVersion(finalVersion) && !versions.isEmpty()) { - versions.sort(versionComparator); - return versions.get(versions.size() - 1); - } - return versionComparator.upgrade(finalVersion, versions).orElse(null); - } catch (IllegalStateException e) { - // this can happen when we encounter exotic versions - return null; - } - } - @Value public static class Accumulator { Set projectArtifacts = new HashSet<>(); diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/trait/MavenDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/trait/MavenDependency.java index 45b0257773d..01ea73ac47c 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/trait/MavenDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/trait/MavenDependency.java @@ -19,16 +19,27 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; -import org.openrewrite.maven.tree.Dependency; -import org.openrewrite.maven.tree.ResolvedDependency; -import org.openrewrite.maven.tree.Scope; +import org.openrewrite.ExecutionContext; +import org.openrewrite.maven.MavenDownloadingException; +import org.openrewrite.maven.MavenExecutionContextView; +import org.openrewrite.maven.MavenSettings; +import org.openrewrite.maven.internal.MavenPomDownloader; +import org.openrewrite.maven.table.MavenMetadataFailures; +import org.openrewrite.maven.tree.*; +import org.openrewrite.semver.ExactVersion; +import org.openrewrite.semver.LatestPatch; +import org.openrewrite.semver.Semver; +import org.openrewrite.semver.VersionComparator; import org.openrewrite.trait.Trait; import org.openrewrite.xml.XPathMatcher; import org.openrewrite.xml.tree.Xml; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; +import static java.util.Collections.emptyMap; import static org.openrewrite.internal.StringUtils.matchesGlob; @Value @@ -41,6 +52,74 @@ public class MavenDependency implements Trait { @Getter ResolvedDependency resolvedDependency; + public static @Nullable String findNewerVersion( + String groupId, + String artifactId, + @Nullable String currentVersion, + MavenResolutionResult mrr, + MavenMetadataFailures metadataFailures, + VersionComparator versionComparator, + ExecutionContext ctx) throws MavenDownloadingException { + String finalVersion = !Semver.isVersion(currentVersion) ? "0.0.0" : currentVersion; + + // in the case of "latest.patch", a new version can only be derived if the + // current version is a semantic version + if (versionComparator instanceof LatestPatch && !versionComparator.isValid(finalVersion, finalVersion)) { + return null; + } + + try { + MavenExecutionContextView mctx = MavenExecutionContextView.view(ctx); + MavenMetadata mavenMetadata = metadataFailures.insertRows(ctx, () -> new MavenPomDownloader( + emptyMap(), ctx, + Optional.ofNullable(mctx.getSettings()) + .orElse(mrr.getMavenSettings()), + Optional.ofNullable(mctx.getSettings()) + .map(MavenSettings::getActiveProfiles) + .map(MavenSettings.ActiveProfiles::getActiveProfiles) + .orElse(mrr.getActiveProfiles())) + .downloadMetadata(new GroupArtifact(groupId, artifactId), null, mrr.getPom().getRepositories())); + List versions = new ArrayList<>(); + for (String v : mavenMetadata.getVersioning().getVersions()) { + if (versionComparator.isValid(finalVersion, v)) { + versions.add(v); + } + } + + // Some repositories will have corrupt or incomplete maven metadata which + // prevents an upgrade even to a fixed version. For example this metadata file is missing all versions after 2019: + // https://repository.mapr.com/nexus/content/groups/mapr-public/org/apache/hbase/hbase-annotations/maven-metadata.xml + if (versionComparator instanceof ExactVersion) { + String exactVersion = ((ExactVersion) versionComparator).getVersion(); + if (!versions.contains(exactVersion)) { + try { + // This is a best effort attempt to see if the pom is there anyway, in spite of the + // fact that it's not in the metadata. Usually it won't be, only in situations like the + // MapR repository mentioned in the comment above will it be. + Pom pom = new MavenPomDownloader(emptyMap(), ctx, + mrr.getMavenSettings(), mrr.getActiveProfiles()).download(new GroupArtifactVersion(groupId, artifactId, ((ExactVersion) versionComparator).getVersion()), + null, null, mrr.getPom().getRepositories()); + if (pom.getGav().getVersion().equals(exactVersion)) { + return exactVersion; + } + } catch (MavenDownloadingException e) { + return null; + } + } + } + + // handle upgrades from non semver versions like "org.springframework.cloud:spring-cloud-dependencies:Camden.SR5" + if (!Semver.isVersion(finalVersion) && !versions.isEmpty()) { + versions.sort(versionComparator); + return versions.get(versions.size() - 1); + } + return versionComparator.upgrade(finalVersion, versions).orElse(null); + } catch (IllegalStateException e) { + // this can happen when we encounter exotic versions + return null; + } + } + public static class Matcher extends MavenTraitMatcher { @Nullable protected String groupId; diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/AddManagedDependencyTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/AddManagedDependencyTest.java index 4b1f1bacdef..d09794d54cc 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/AddManagedDependencyTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/AddManagedDependencyTest.java @@ -251,4 +251,52 @@ void propertiesAsGAVCoordinates() { ) ); } + + @Test + void versionSelectionTakesExistingIntoAccount() { + rewriteRun( + spec -> spec.recipes(new AddManagedDependency("com.fasterxml.jackson.core", "jackson-databind", "latest.patch", null, + null, null, null, null, null, true)), + //language=xml + pomXml( + """ + + com.mycompany.app + my-app + 1 + + + org.openrewrite + rewrite-java + 7.0.0 + + + + """, + """ + + com.mycompany.app + my-app + 1 + + + + com.fasterxml.jackson.core + jackson-databind + 2.12.7.2 + + + + + + org.openrewrite + rewrite-java + 7.0.0 + + + + """ + ) + ); + } } From 05352974ef6869d7f795f4b344730b19f0561980 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sun, 15 Sep 2024 11:54:53 +0200 Subject: [PATCH 49/82] `SemanticallyEqual` should allow for different lambda parameter names (#4494) * Fix minor test bug Caused a minor test attribution error. * `SemanticallyEqual` should allow for different lambda parameter names While comparing two lambdas for semantical equality, the lambda parameter names should be ignored. This actually also applies to other variables (e.g. variables declared in a block such as a lambda body), but that is out of scope for this PR. This will allow Refaster recipes to match a lambda against other lambdas using different parameter names. --- .../java/search/SemanticallyEqualTest.java | 22 ++++++++ .../java/service/AnnotationServiceTest.java | 8 +-- .../java/JavaTemplateSemanticallyEqual.java | 16 +++--- .../java/search/SemanticallyEqual.java | 53 ++++++++++++++++--- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/search/SemanticallyEqualTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/search/SemanticallyEqualTest.java index b4de19e17ba..0e4f98aa4f1 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/search/SemanticallyEqualTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/search/SemanticallyEqualTest.java @@ -299,6 +299,28 @@ class T { ); } + @Test + void lambdaParameterNames() { + assertExpressionsEqual( + """ + import java.util.Comparator; + class T { + Comparator a = (x1, y1) -> x1 - y1; + Comparator b = (x2, y2) -> x2 - y2; + } + """ + ); + assertExpressionsNotEqual( + """ + import java.util.Comparator; + class T { + Comparator a = (x1, y1) -> x1 - y1; + Comparator b = (x2, y2) -> y2 - x2; + } + """ + ); + } + @Nested class Generics { @Test diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/service/AnnotationServiceTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/service/AnnotationServiceTest.java index 0bcc10a3d1d..4c7896dbce1 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/service/AnnotationServiceTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/service/AnnotationServiceTest.java @@ -34,7 +34,7 @@ void classAnnotations() { java( """ import javax.annotation.processing.Generated; - + @SuppressWarnings("all") public @Generated("foo") class T {} """, @@ -68,7 +68,7 @@ void annotatedType() { import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; - + class T { public @A1 Integer @A2 [] arg; } @@ -159,9 +159,9 @@ void fieldAccessAnnotations() { import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; - + class T { - java. lang. @Ann Map arg; + java. lang. @Ann Integer arg; } @Retention(RetentionPolicy.RUNTIME) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/JavaTemplateSemanticallyEqual.java b/rewrite-java/src/main/java/org/openrewrite/java/JavaTemplateSemanticallyEqual.java index 2215355e8ce..4416c6c0a24 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/JavaTemplateSemanticallyEqual.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/JavaTemplateSemanticallyEqual.java @@ -73,18 +73,22 @@ private static J[] createTemplateParameters(String code) { substituted = propertyPlaceholderHelper.replacePlaceholders(substituted, key -> { String s; if (!key.isEmpty()) { - TemplateParameterParser parser = new TemplateParameterParser(new CommonTokenStream(new TemplateParameterLexer( - CharStreams.fromString(key)))); - - parser.removeErrorListeners(); - parser.addErrorListener(new BaseErrorListener() { + BaseErrorListener errorListener = new BaseErrorListener() { @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { throw new IllegalArgumentException( String.format("Syntax error at line %d:%d %s.", line, charPositionInLine, msg), e); } - }); + }; + + TemplateParameterLexer lexer = new TemplateParameterLexer(CharStreams.fromString(key)); + lexer.removeErrorListeners(); + lexer.addErrorListener(errorListener); + + TemplateParameterParser parser = new TemplateParameterParser(new CommonTokenStream(lexer)); + parser.removeErrorListeners(); + parser.addErrorListener(errorListener); TemplateParameterParser.MatcherPatternContext ctx = parser.matcherPattern(); if (ctx.typedPattern() == null) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java b/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java index e2631a72234..0bcf9b648c9 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java @@ -21,9 +21,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; -import java.util.EnumSet; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -59,6 +57,8 @@ protected static class SemanticallyEqualVisitor extends JavaIsoVisitor { private final boolean compareMethodArguments; protected final AtomicBoolean isEqual = new AtomicBoolean(true); + private final Deque> variableScope = new ArrayDeque<>(); + private final Set seen = new HashSet<>(); public SemanticallyEqualVisitor(boolean compareMethodArguments) { this.compareMethodArguments = compareMethodArguments; @@ -120,6 +120,29 @@ protected void visitList(@Nullable List list1, @Nullable List()); + } + return tree; + } + + @Override + public @Nullable J postVisit(J tree, J j) { + if (declaresVariableScope(tree)) { + variableScope.pop(); + } + return tree; + } + + protected boolean declaresVariableScope(J tree) { + if (tree instanceof J.Lambda) { + return true; + } + return false; + } + @Override public Expression visitExpression(Expression expression, J j) { if (isEqual.get()) { @@ -683,6 +706,16 @@ public J.Identifier visitIdentifier(J.Identifier identifier, J j) { } J.Identifier compareTo = (J.Identifier) j; + if (identifier.getFieldType() != null) { + Map scope = variableScope.peek(); + if (scope != null && scope.containsKey(identifier.getSimpleName()) && scope.get(identifier.getSimpleName()).equals(compareTo.getSimpleName())) { + return identifier; + } + } + if (TypeUtils.isWellFormedType(identifier.getType(), seen) && !TypeUtils.isOfType(identifier.getType(), compareTo.getType())) { + isEqual.set(false); + return identifier; + } if (!identifier.getSimpleName().equals(compareTo.getSimpleName())) { isEqual.set(false); return identifier; @@ -784,8 +817,8 @@ public J.Lambda visitLambda(J.Lambda lambda, J j) { isEqual.set(false); return lambda; } + visitList(lambda.getParameters().getParameters(), compareTo.getParameters().getParameters()); visit(lambda.getBody(), compareTo.getBody()); - this.visitList(lambda.getParameters().getParameters(), compareTo.getParameters().getParameters()); } return lambda; } @@ -911,7 +944,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, J j) JavaType.FullyQualified methodDeclaringType = method.getMethodType().getDeclaringType(); JavaType.FullyQualified compareToDeclaringType = compareTo.getMethodType().getDeclaringType(); if (!TypeUtils.isAssignableTo(methodDeclaringType instanceof JavaType.Parameterized ? - ((JavaType.Parameterized) methodDeclaringType).getType() : methodDeclaringType, + ((JavaType.Parameterized) methodDeclaringType).getType() : methodDeclaringType, compareToDeclaringType instanceof JavaType.Parameterized ? ((JavaType.Parameterized) compareToDeclaringType).getType() : compareToDeclaringType)) { isEqual.set(false); @@ -1360,8 +1393,14 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations } J.VariableDeclarations.NamedVariable compareTo = (J.VariableDeclarations.NamedVariable) j; - if (!variable.getSimpleName().equals(compareTo.getSimpleName()) || - !TypeUtils.isOfType(variable.getType(), compareTo.getType()) || + Map scope = variableScope.peek(); + if (scope != null) { + scope.put(variable.getSimpleName(), compareTo.getSimpleName()); + } else if (!variable.getSimpleName().equals(compareTo.getSimpleName())) { + isEqual.set(false); + return variable; + } + if (!TypeUtils.isOfType(variable.getType(), compareTo.getType()) || nullMissMatch(variable.getInitializer(), compareTo.getInitializer())) { isEqual.set(false); return variable; From c39a0836c7073104c465ce2c592533948b8fbd3c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 16 Sep 2024 11:38:04 +0200 Subject: [PATCH 50/82] Deprecate `RemoveRedundantDependencyVersions.onlyIfVersionsMatch` in docs --- .../openrewrite/maven/RemoveRedundantDependencyVersions.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java index deda626fd3b..c6c098b633a 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java @@ -57,7 +57,8 @@ public class RemoveRedundantDependencyVersions extends Recipe { String artifactPattern; @Option(displayName = "Only if versions match", - description = "Only remove the explicit version if it exactly matches the managed dependency version. " + + description = "Deprecated; use `onlyIfManagedVersionIs` instead. " + + "Only remove the explicit version if it exactly matches the managed dependency version. " + "When `false` explicit versions will be removed if they are older than or equal to the managed dependency version. " + "Default `true`.", required = false) From 525f36804b46fd803de2b2010d58dbfe811d99e9 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Mon, 16 Sep 2024 20:17:44 +0200 Subject: [PATCH 51/82] Improve handling of inner classes (#4483) * Fix issue with inner classes * Additional testcase * Fix some edge cases * Properly name test methods * Apply formatter and extract common target to variable * Move testcases so they are in appropriate places * Should take multiple nested classes into account --------- Co-authored-by: Tim te Beek --- .../openrewrite/java/OrderImportsTest.java | 108 ++++++++++++------ .../java/RemoveUnusedImportsTest.java | 33 ++++++ .../openrewrite/java/RemoveUnusedImports.java | 21 +++- .../java/style/ImportLayoutStyle.java | 7 +- 4 files changed, 123 insertions(+), 46 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java index 5988de0515b..85e6f79bfd7 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java @@ -22,14 +22,12 @@ import org.openrewrite.style.NamedStyles; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; import static org.openrewrite.Tree.randomId; -import static org.openrewrite.java.Assertions.addTypesToSourceSet; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.java.Assertions.srcMainJava; -import static org.openrewrite.java.Assertions.version; +import static org.openrewrite.java.Assertions.*; class OrderImportsTest implements RewriteTest { @@ -53,7 +51,7 @@ void multipleClassesWithTheSameNameButDifferentPackages() { """ import java.awt.List; import java.util.List; - + class Test {} """ ) @@ -87,9 +85,9 @@ void blankLinesNotFollowedByBlockArentAdded() { java( """ import java.util.List; - + import static java.util.Collections.*; - + class A {} """ ) @@ -133,7 +131,7 @@ void unfoldStar() { java( """ import java.util.*; - + class A { List list; List list2; @@ -141,7 +139,7 @@ class A { """, """ import java.util.List; - + class A { List list; List list2; @@ -158,7 +156,7 @@ void unfoldStarMultiple() { java( """ import java.util.*; - + class A { List list; Map map; @@ -167,7 +165,7 @@ class A { """ import java.util.List; import java.util.Map; - + class A { List list; Map map; @@ -196,18 +194,18 @@ void unfoldStaticStar() { java( """ import java.util.List; - + import static java.util.Collections.*; - + class A { List list = emptyList(); } """, """ import java.util.List; - + import static java.util.Collections.emptyList; - + class A { List list = emptyList(); } @@ -270,25 +268,25 @@ void springCloudFormat() { import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.zip.GZIPOutputStream; - + import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; - + import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.logging.Log; import reactor.core.publisher.Mono; - + import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.server.ServerWebExchange; - + import static java.util.Arrays.stream; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.toAsyncPredicate; - + class A {} """ ) @@ -340,7 +338,7 @@ void preservesStaticStarImportWhenRemovingUnused() { java( """ import static java.util.Collections.*; - + class Test { Object[] o = new Object[] { emptyList(), emptyMap(), emptySet() }; } @@ -362,7 +360,7 @@ public class MyCollections extends java.util.Collections {} java( """ import static my.MyCollections.*; - + class Test { Object[] o = new Object[] { emptyList(), emptyMap(), emptySet() }; } @@ -378,7 +376,7 @@ void preservesStaticMethodArguments() { java( """ import static java.util.Collections.*; - + class Test { Object[] o = new Object[] { emptyList(), emptyMap(), emptySet() }; } @@ -470,9 +468,9 @@ class Test { """, """ import static java.util.Collections.singletonList; - + import java.util.List; - + class Test { } """ @@ -510,10 +508,10 @@ void detectBlockPattern() { // org.slf4j should be detected as a block pattern, and not be moved to all other imports. import org.slf4j.Logger; import org.slf4j.LoggerFactory; - + import java.util.Arrays; import java.util.List; - + public class C { } """ @@ -554,14 +552,14 @@ void doNotFoldImports() { import java.util.HashSet; import java.util.List; import java.util.Set; - + import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; - + public class C { } """ @@ -668,12 +666,12 @@ void foldPackageWithExistingImports() { singletonList( new NamedStyles( randomId(), "test", "test", "test", emptySet(), singletonList( - ImportLayoutStyle.builder() - .packageToFold("java.util.*", false) - .importAllOthers() - .importStaticAllOthers() - .build() - ) + ImportLayoutStyle.builder() + .packageToFold("java.util.*", false) + .importAllOthers() + .importStaticAllOthers() + .build() + ) ) ))), java( @@ -686,4 +684,44 @@ void foldPackageWithExistingImports() { ) ); } + + @Test + void nestedInnerClass() { + // language=java + rewriteRun( + spec -> spec.recipe(new OrderImports(true)), + java( + """ + import org.openrewrite.java.tree.JContainer; + import org.openrewrite.java.tree.JLeftPadded; + import org.openrewrite.java.tree.JRightPadded; + import org.openrewrite.java.tree.JavaType; + import org.openrewrite.java.tree.JavaType.Variable; + import org.openrewrite.java.tree.Space; + + public class Foo { + Variable myVariable = null; + JLeftPadded myLeftPadded = null; + JRightPadded myRightPadded = null; + JContainer myContainer = null; + Space mySpace = null; + JavaType.Variable myVariable = null; + } + """, + """ + import org.openrewrite.java.tree.*; + import org.openrewrite.java.tree.JavaType.Variable; + + public class Foo { + Variable myVariable = null; + JLeftPadded myLeftPadded = null; + JRightPadded myRightPadded = null; + JContainer myContainer = null; + Space mySpace = null; + JavaType.Variable myVariable = null; + } + """ + ) + ); + } } diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java index f269a88cc19..686b4ade52c 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java @@ -2035,4 +2035,37 @@ class CImpl { ) ); } + + @Test + void staticNestedInnerClass() { + // language=java + rewriteRun( + java( + """ + package a; + + public class A { + public final class B {} + public static class C {} + } + """, + SourceSpec::skip), + java( + """ + import a.*; + + public class Foo { + A method(A.B ab, A.C ac) {} + } + """, + """ + import a.A; + + public class Foo { + A method(A.B ab, A.C ac) {} + } + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java b/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java index 86e99d5f313..10b2a19e74e 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java @@ -106,12 +106,20 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon for (JavaType typeParameter : parameterized.getTypeParameters()) { JavaType.FullyQualified fq = TypeUtils.asFullyQualified(typeParameter); if (fq != null) { - typesByPackage.computeIfAbsent(fq.getPackageName(), f -> new HashSet<>()).add(fq); + typesByPackage.computeIfAbsent( + fq.getOwningClass() == null ? + fq.getPackageName() : + toFullyQualifiedName(fq.getOwningClass().getFullyQualifiedName()), + f -> new HashSet<>()).add(fq); } } } else if (javaType instanceof JavaType.FullyQualified) { JavaType.FullyQualified fq = (JavaType.FullyQualified) javaType; - typesByPackage.computeIfAbsent(fq.getPackageName(), f -> new HashSet<>()).add(fq); + typesByPackage.computeIfAbsent( + fq.getOwningClass() == null ? + fq.getPackageName() : + toFullyQualifiedName(fq.getOwningClass().getFullyQualifiedName()), + f -> new HashSet<>()).add(fq); } } @@ -152,7 +160,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon SortedSet targetMethodsAndFields = methodsAndFieldsByTypeName.get(modifiedTarget); Set staticClasses = null; - for (JavaType.FullyQualified maybeStatic : typesByPackage.getOrDefault(elem.getPackageName(), emptySet())) { + for (JavaType.FullyQualified maybeStatic : typesByPackage.getOrDefault(target, emptySet())) { if (maybeStatic.getOwningClass() != null && outerType.startsWith(maybeStatic.getOwningClass().getFullyQualifiedName())) { if (staticClasses == null) { staticClasses = new HashSet<>(); @@ -207,8 +215,9 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon changed = true; } } else { - Set types = typesByPackage.getOrDefault(elem.getPackageName(), new HashSet<>()); - Set typesByFullyQualifiedClassPath = typesByPackage.getOrDefault(toFullyQualifiedName(elem.getPackageName()), new HashSet<>()); + String target = qualid.getTarget().toString(); + Set types = typesByPackage.getOrDefault(target, new HashSet<>()); + Set typesByFullyQualifiedClassPath = typesByPackage.getOrDefault(toFullyQualifiedName(target), new HashSet<>()); Set combinedTypes = Stream.concat(types.stream(), typesByFullyQualifiedClassPath.stream()) .collect(Collectors.toSet()); JavaType.FullyQualified qualidType = TypeUtils.asFullyQualified(elem.getQualid().getType()); @@ -236,7 +245,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon changed = true; } else { - usedWildcardImports.add(elem.getQualid().getTarget().toString()); + usedWildcardImports.add(target); } } else if (combinedTypes.stream().noneMatch(c -> { if ("*".equals(elem.getQualid().getSimpleName())) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/ImportLayoutStyle.java b/rewrite-java/src/main/java/org/openrewrite/java/style/ImportLayoutStyle.java index d662b3403ae..b03fe350d51 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/ImportLayoutStyle.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/ImportLayoutStyle.java @@ -788,11 +788,8 @@ private static String packageOrOuterClassName(JRightPadded anImport) { if (anImport.getElement().isStatic()) { return typeName; } else { - String className = anImport.getElement().getClassName(); - if (className.contains("$")) { - return anImport.getElement().getPackageName() + "." + - className.substring(0, className.lastIndexOf('$')) - .replace('$', '.'); + if (typeName.contains("$")) { + return typeName.substring(0, typeName.lastIndexOf('$')).replace('$', '.'); } return anImport.getElement().getPackageName(); } From 41ed4721fad07c7f525eb654f3c30e261db82bd1 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Mon, 16 Sep 2024 14:36:13 -0700 Subject: [PATCH 52/82] Avoid NPE when dependency declaration version number is not explicitly referenced. --- .../java/org/openrewrite/gradle/UpgradeDependencyVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index 15b753b0b58..2bdf0a27197 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -214,7 +214,7 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) break; } } - if (groupId == null || artifactId == null) { + if (groupId == null || artifactId == null || version == null) { return m; } From b7ae2d4fbc80070acac4075f4b74de9e8a7b31cf Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Mon, 16 Sep 2024 15:03:27 -0700 Subject: [PATCH 53/82] Further efforts to avoid NPE around GradleProject.getBuildScript() from old LSTs --- .../gradle/marker/GradleProject.java | 16 +---------- .../gradle/plugins/ChangePlugin.java | 20 +++++-------- .../gradle/trait/GradleDependency.java | 5 ++++ .../gradle/search/FindPluginsTest.java | 28 ++++++++----------- 4 files changed, 25 insertions(+), 44 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java index 5237e8e135c..81efa107628 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java @@ -82,21 +82,6 @@ public class GradleProject implements Marker, Serializable { @Builder.Default GradleBuildscript buildscript = new GradleBuildscript(randomId(), emptyList(), emptyMap()); - // Backwards compatibility to ease convoluted release process with rewrite-gradle-tooling-model - public GradleProject( - UUID id, - String group, - String name, - String version, - String path, - List plugins, - List mavenRepositories, - List mavenPluginRepositories, - Map nameToConfiguration - ) { - this(id, group, name, version, path, plugins, mavenRepositories, mavenPluginRepositories, nameToConfiguration, null); - } - /** * Get a list of Maven plugin repositories. * @@ -105,6 +90,7 @@ public GradleProject( */ @Deprecated public List getMavenPluginRepositories() { + //noinspection ConstantValue if (buildscript != null) { return buildscript.getMavenRepositories(); } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java index 252ac358736..fe26fee92cc 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/ChangePlugin.java @@ -36,7 +36,6 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.maven.MavenDownloadingException; import org.openrewrite.maven.tree.GroupArtifact; -import org.openrewrite.maven.tree.MavenRepository; import org.openrewrite.semver.Semver; import java.util.Collections; @@ -58,12 +57,12 @@ public class ChangePlugin extends Recipe { example = "org.openrewrite.rewrite") String pluginId; - @Option(displayName = "New Plugin ID", + @Option(displayName = "New plugin ID", description = "The new Gradle plugin id.", example = "org.openrewrite.rewrite") String newPluginId; - @Option(displayName = "New Plugin Version", + @Option(displayName = "New plugin version", description = "An exact version number or node-style semver selector used to select the version number. " + "You can also use `latest.release` for the latest available version and `latest.patch` if " + "the current version is a valid semantic version. For more details, you can look at the documentation " + @@ -105,7 +104,9 @@ public TreeVisitor getVisitor() { return Preconditions.check( Preconditions.or(new IsBuildGradle<>(), new IsSettingsGradle<>()), new GroovyIsoVisitor() { + @Nullable GradleProject gradleProject; + @Nullable GradleSettings gradleSettings; @Override @@ -129,7 +130,7 @@ public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionCon return plugin; })); g = g.withMarkers(g.getMarkers().setByType(updatedGp)); - } else { + } else if (gradleSettings != null) { GradleSettings updatedGs = gradleSettings.withPlugins(ListUtils.map(gradleSettings.getPlugins(), plugin -> { if (pluginId.equals(plugin.getId())) { return new GradlePluginDescriptor("unknown", newPluginId); @@ -159,7 +160,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu private J.MethodInvocation maybeUpdateVersion(J.MethodInvocation m, ExecutionContext ctx) { J.MethodInvocation select = (J.MethodInvocation) m.getSelect(); - if (!pluginId.equals(((J.Literal) select.getArguments().get(0)).getValue())) { + if (select == null || !pluginId.equals(((J.Literal) select.getArguments().get(0)).getValue())) { return m; } @@ -217,7 +218,7 @@ private J.MethodInvocation maybeUpdateApplySyntax(J.MethodInvocation m) { } G.MapEntry entry = (G.MapEntry) args.get(0); - if (!(entry.getKey() instanceof J.Literal) && !(entry.getValue() instanceof J.Literal)) { + if (!(entry.getKey() instanceof J.Literal) || !(entry.getValue() instanceof J.Literal)) { return m; } @@ -244,13 +245,6 @@ private J.MethodInvocation maybeUpdateApplySyntax(J.MethodInvocation m) { entry = entry.withValue(ChangeStringLiteral.withStringValue(valueLiteral, newPluginId)); return m.withArguments(ListUtils.concat(entry, args.subList(1, args.size()))); } - - private List getPluginRepositories() { - if (gradleProject != null) { - return gradleProject.getBuildscript().getMavenRepositories(); - } - return gradleSettings.getBuildscript().getMavenRepositories(); - } } ); } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java index 1f20c8e92e2..83a58b74924 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java @@ -140,6 +140,11 @@ public Matcher artifactId(@Nullable String artifactId) { private static @Nullable GradleDependencyConfiguration getConfiguration(GradleProject gradleProject, J.MethodInvocation methodInvocation) { String methodName = methodInvocation.getSimpleName(); if (methodName.equals("classpath")) { + // Minimize incompatibility with old LSTs missing this field + //noinspection ConstantValue + if (gradleProject.getBuildscript() == null) { + return null; + } return gradleProject.getBuildscript().getConfiguration(methodName); } else { return gradleProject.getConfiguration(methodName); diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/search/FindPluginsTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/search/FindPluginsTest.java index f27b1dc5aa5..c4f9f476ec5 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/search/FindPluginsTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/search/FindPluginsTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; -import org.openrewrite.Tree; import org.openrewrite.gradle.marker.GradlePluginDescriptor; import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.test.RecipeSpec; @@ -54,11 +53,11 @@ void findPlugin() { """, spec -> spec .beforeRecipe(cu -> assertThat(FindPlugins.find(cu, "org.openrewrite.rewrite")) - .isNotEmpty() - .anySatisfy(p -> { - assertThat(p.getPluginId()).isEqualTo("org.openrewrite.rewrite"); - assertThat(p.getVersion()).isEqualTo("6.18.0"); - })) + .isNotEmpty() + .anySatisfy(p -> { + assertThat(p.getPluginId()).isEqualTo("org.openrewrite.rewrite"); + assertThat(p.getVersion()).isEqualTo("6.18.0"); + })) ) ); } @@ -69,16 +68,13 @@ void findPluginFromGradleProjectMarker() { text( "stand-in for a kotlin gradle script", "~~>stand-in for a kotlin gradle script", - spec -> spec.markers(new GradleProject( - Tree.randomId(), - "group", - "name", - "version", - "path", - Collections.singletonList(new GradlePluginDescriptor("org.openrewrite.gradle.GradlePlugin", "org.openrewrite.rewrite")), - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyMap()) + spec -> spec.markers(GradleProject.builder() + .group("group") + .name("name") + .version("version") + .path("path") + .plugins(Collections.singletonList(new GradlePluginDescriptor("org.openrewrite.gradle.GradlePlugin", "org.openrewrite.rewrite"))) + .build() ) ) ); From a116a957fc045f40eb2e3412fd22aadb64e6fd93 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 17 Sep 2024 10:28:43 +0200 Subject: [PATCH 54/82] refactor: Order imports (#4496) * refactor: Order imports Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.OrderImports?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sidmFsdWUiOiJUcnVlIiwibmFtZSI6InJlbW92ZVVudXNlZCJ9XQ== Co-authored-by: Moderne * Apply suggestions from code review --------- Co-authored-by: Moderne --- .../org/openrewrite/marker/GitProvenanceTest.java | 1 - .../openrewrite/gradle/marker/GradleBuildscript.java | 5 ++++- .../org/openrewrite/groovy/GroovyTypeMapping.java | 4 +++- .../openrewrite/java/ReloadableJava8TypeMapping.java | 4 +++- .../org/openrewrite/java/JavaTypeMappingTest.java | 4 +++- .../internal/grammar/AnnotationSignatureLexer.java | 10 ++++------ .../internal/grammar/AnnotationSignatureParser.java | 12 +++++++----- .../internal/grammar/TemplateParameterLexer.java | 10 ++++------ .../internal/grammar/TemplateParameterParser.java | 12 +++++++----- .../main/java/org/openrewrite/json/JsonParser.java | 5 ++++- .../src/main/java/org/openrewrite/xml/XmlParser.java | 5 ++++- .../openrewrite/xml/internal/grammar/XMLLexer.java | 10 ++++------ .../openrewrite/xml/internal/grammar/XMLParser.java | 12 +++++++----- 13 files changed, 54 insertions(+), 40 deletions(-) diff --git a/rewrite-core/src/test/java/org/openrewrite/marker/GitProvenanceTest.java b/rewrite-core/src/test/java/org/openrewrite/marker/GitProvenanceTest.java index 4fe19dd901a..434a59d62fd 100644 --- a/rewrite-core/src/test/java/org/openrewrite/marker/GitProvenanceTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/marker/GitProvenanceTest.java @@ -48,7 +48,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import static com.fasterxml.jackson.core.JsonParser.Feature.IGNORE_UNDEFINED; import static com.fasterxml.jackson.core.JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION; import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java index 81cee15799b..22f7667b785 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleBuildscript.java @@ -23,7 +23,10 @@ import org.openrewrite.maven.tree.MavenRepository; import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; diff --git a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyTypeMapping.java b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyTypeMapping.java index 56eb0efdff9..872160fa148 100644 --- a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyTypeMapping.java +++ b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyTypeMapping.java @@ -30,7 +30,9 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.*; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.CONTRAVARIANT; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.COVARIANT; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.INVARIANT; class GroovyTypeMapping implements JavaTypeMapping { private final GroovyAstTypeSignatureBuilder signatureBuilder = new GroovyAstTypeSignatureBuilder(); diff --git a/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8TypeMapping.java b/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8TypeMapping.java index 14787c651c2..7bb5a56544c 100644 --- a/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8TypeMapping.java +++ b/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8TypeMapping.java @@ -35,7 +35,9 @@ import java.util.stream.Collectors; import static java.util.Collections.singletonList; -import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.*; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.CONTRAVARIANT; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.COVARIANT; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.INVARIANT; @RequiredArgsConstructor class ReloadableJava8TypeMapping implements JavaTypeMapping { diff --git a/rewrite-java-test/src/main/java/org/openrewrite/java/JavaTypeMappingTest.java b/rewrite-java-test/src/main/java/org/openrewrite/java/JavaTypeMappingTest.java index f88523ea5a8..591d897bfc3 100644 --- a/rewrite-java-test/src/main/java/org/openrewrite/java/JavaTypeMappingTest.java +++ b/rewrite-java-test/src/main/java/org/openrewrite/java/JavaTypeMappingTest.java @@ -25,7 +25,9 @@ import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; -import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.*; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.CONTRAVARIANT; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.COVARIANT; +import static org.openrewrite.java.tree.JavaType.GenericTypeVariable.Variance.INVARIANT; /** * Based on type attribution mappings of [JavaTypeGoat]. diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureLexer.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureLexer.java index b7de3acdab7..c5f2864b95e 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureLexer.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureLexer.java @@ -16,13 +16,11 @@ // Generated from java-escape by ANTLR 4.11.1 package org.openrewrite.java.internal.grammar; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Lexer; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) public class AnnotationSignatureLexer extends Lexer { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureParser.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureParser.java index 914f7238853..a36d8d41798 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureParser.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/AnnotationSignatureParser.java @@ -16,13 +16,15 @@ // Generated from java-escape by ANTLR 4.11.1 package org.openrewrite.java.internal.grammar; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; -import java.util.ArrayList; -import java.util.Iterator; import java.util.List; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterLexer.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterLexer.java index 9eed4532351..195399da966 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterLexer.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterLexer.java @@ -16,13 +16,11 @@ // Generated from java-escape by ANTLR 4.11.1 package org.openrewrite.java.internal.grammar; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Lexer; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) public class TemplateParameterLexer extends Lexer { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterParser.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterParser.java index ac2dcac7364..01d3d651a7b 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterParser.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/grammar/TemplateParameterParser.java @@ -16,13 +16,15 @@ // Generated from java-escape by ANTLR 4.11.1 package org.openrewrite.java.internal.grammar; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; -import java.util.ArrayList; -import java.util.Iterator; import java.util.List; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) diff --git a/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java b/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java index e3494066a2e..7f654cd33e9 100755 --- a/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java +++ b/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java @@ -18,7 +18,10 @@ import org.antlr.v4.runtime.*; import org.intellij.lang.annotations.Language; import org.jspecify.annotations.Nullable; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Parser; +import org.openrewrite.SourceFile; import org.openrewrite.Parser; import org.openrewrite.json.internal.JsonParserVisitor; import org.openrewrite.json.internal.grammar.JSON5Lexer; diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java index e9e323443a8..098a5279fc0 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java @@ -18,7 +18,10 @@ import org.antlr.v4.runtime.*; import org.intellij.lang.annotations.Language; import org.jspecify.annotations.Nullable; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Parser; +import org.openrewrite.SourceFile; import org.openrewrite.Parser; import org.openrewrite.internal.EncodingDetectingInputStream; import org.openrewrite.tree.ParseError; diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLLexer.java b/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLLexer.java index 066e1a879e3..3756f23a97e 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLLexer.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLLexer.java @@ -16,13 +16,11 @@ // Generated from java-escape by ANTLR 4.11.1 package org.openrewrite.xml.internal.grammar; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Lexer; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) public class XMLLexer extends Lexer { diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLParser.java b/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLParser.java index fef0184a3a9..31beb42c95a 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLParser.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/internal/grammar/XMLParser.java @@ -16,13 +16,15 @@ // Generated from java-escape by ANTLR 4.11.1 package org.openrewrite.xml.internal.grammar; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; -import java.util.ArrayList; -import java.util.Iterator; import java.util.List; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue"}) From a48372e592fc0a8fa1f751c5b802fb3037748f60 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Tue, 17 Sep 2024 20:40:47 +0200 Subject: [PATCH 55/82] Fix regression in `SemanticallyEqual` When comparing identifiers don't compare the types until it is clearer what is required here. Currently, this causes the `CombineSemanticallyEqualCatchBlocks` recipe to break when comparing the bodies of catch blocks, as the type of the variable representing the exception is different. --- .../java/org/openrewrite/java/search/SemanticallyEqual.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java b/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java index 0bcf9b648c9..7513f29024d 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java @@ -712,10 +712,6 @@ public J.Identifier visitIdentifier(J.Identifier identifier, J j) { return identifier; } } - if (TypeUtils.isWellFormedType(identifier.getType(), seen) && !TypeUtils.isOfType(identifier.getType(), compareTo.getType())) { - isEqual.set(false); - return identifier; - } if (!identifier.getSimpleName().equals(compareTo.getSimpleName())) { isEqual.set(false); return identifier; From 3ec41149c2c80a3266a5666e6705f617c56d8a76 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 17 Sep 2024 13:12:09 -0700 Subject: [PATCH 56/82] Further and further efforts to avoid NPE around GradleProject.getBuildScript() from old LSTs --- .../org/openrewrite/gradle/marker/GradleProject.java | 9 +++++++++ .../org/openrewrite/gradle/marker/GradleSettings.java | 9 +++++++++ .../org/openrewrite/gradle/trait/GradleDependency.java | 5 ----- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java index 81efa107628..01afc30deb8 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleProject.java @@ -82,6 +82,15 @@ public class GradleProject implements Marker, Serializable { @Builder.Default GradleBuildscript buildscript = new GradleBuildscript(randomId(), emptyList(), emptyMap()); + public GradleBuildscript getBuildscript() { + // Temporary workaround for better compatibility with old LSTs that don't have a buildscript field yet. + //noinspection ConstantValue + if (buildscript == null) { + return new GradleBuildscript(randomId(), emptyList(), emptyMap()); + } + return buildscript; + } + /** * Get a list of Maven plugin repositories. * diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java index ed3431212c0..08879300a80 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/marker/GradleSettings.java @@ -65,6 +65,15 @@ public GradleSettings( this(id, pluginRepositories, plugins, featurePreviews, null); } + public GradleBuildscript getBuildscript() { + // Temporary workaround for better compatibility with old LSTs that don't have a buildscript field yet. + //noinspection ConstantValue + if (buildscript == null) { + return new GradleBuildscript(randomId(), emptyList(), emptyMap()); + } + return buildscript; + } + public @Nullable Boolean isFeatureEnabled(String name) { return featurePreviews.get(name).getEnabled(); } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java index 83a58b74924..1f20c8e92e2 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/trait/GradleDependency.java @@ -140,11 +140,6 @@ public Matcher artifactId(@Nullable String artifactId) { private static @Nullable GradleDependencyConfiguration getConfiguration(GradleProject gradleProject, J.MethodInvocation methodInvocation) { String methodName = methodInvocation.getSimpleName(); if (methodName.equals("classpath")) { - // Minimize incompatibility with old LSTs missing this field - //noinspection ConstantValue - if (gradleProject.getBuildscript() == null) { - return null; - } return gradleProject.getBuildscript().getConfiguration(methodName); } else { return gradleProject.getConfiguration(methodName); From 7479814e86452d7bb1c65a1043541244b197ada7 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 17 Sep 2024 16:07:43 -0700 Subject: [PATCH 57/82] Add missing project method to DependencyHandlerSpec --- .../src/main/groovy/RewriteGradleProject.groovy | 3 +++ .../gradle/ChangeDependencyConfigurationTest.java | 14 +------------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy b/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy index 3586ffbb603..e3283fd5733 100644 --- a/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy +++ b/rewrite-gradle/src/main/groovy/RewriteGradleProject.groovy @@ -22,6 +22,7 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ExternalDependency +import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.dsl.DependencyConstraintHandler import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.file.FileCollection @@ -38,6 +39,8 @@ import org.gradle.process.JavaForkOptions import org.gradle.process.ProcessForkOptions interface DependencyHandlerSpec extends DependencyHandler { + ProjectDependency project(String path) + ProjectDependency project(String path, String configuration) Dependency annotationProcessor(Object... dependencyNotation) Dependency annotationProcessor(Object dependencyNotation, @DelegatesTo(strategy=Closure.DELEGATE_ONLY, value= ExternalDependency) Closure closure) Dependency api(Object... dependencyNotation) diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java index d5739e7c159..3c1d0433699 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/ChangeDependencyConfigurationTest.java @@ -44,11 +44,9 @@ void changeConfiguration() { plugins { id 'java-library' } - repositories { mavenCentral() } - dependencies { api 'org.openrewrite:rewrite-gradle:latest.release' } @@ -57,11 +55,9 @@ void changeConfiguration() { plugins { id 'java-library' } - repositories { mavenCentral() } - dependencies { implementation 'org.openrewrite:rewrite-gradle:latest.release' } @@ -79,11 +75,9 @@ void worksWithEmptyStringConfig() { plugins { id 'java-library' } - repositories { mavenCentral() } - dependencies { api 'org.openrewrite:rewrite-gradle:latest.release' } @@ -92,11 +86,9 @@ void worksWithEmptyStringConfig() { plugins { id 'java-library' } - repositories { mavenCentral() } - dependencies { implementation 'org.openrewrite:rewrite-gradle:latest.release' } @@ -240,24 +232,20 @@ void worksForProjectDependencies(String group, String artifact) { plugins { id 'java-library' } - repositories { mavenCentral() } - dependencies { api project(":project2") } - """, + """, """ plugins { id 'java-library' } - repositories { mavenCentral() } - dependencies { implementation project(":project2") } From c1f2b4f60209b7a651f3f5980de1b0130cdac505 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 17 Sep 2024 17:47:33 -0700 Subject: [PATCH 58/82] Better handling of version selection with "latest.patch" when the current version is not explicitly provided --- .../gradle/DependencyVersionSelector.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java index e2c549521d2..51328fc91e2 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java @@ -19,6 +19,7 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Incubating; +import org.openrewrite.gradle.marker.GradleDependencyConfiguration; import org.openrewrite.gradle.marker.GradleProject; import org.openrewrite.gradle.marker.GradleSettings; import org.openrewrite.internal.StringUtils; @@ -52,7 +53,7 @@ public class DependencyVersionSelector { GradleSettings gradleSettings; /** - * Used to select a version for a new dependency that has no prior version. + * Used to select a version for a new dependency that has no prior version, or the caller is not sure what the prior version is. * * @param ga The group and artifact of the new dependency. * @param configuration The configuration to select the version for. The configuration influences @@ -69,11 +70,21 @@ public class DependencyVersionSelector { @Nullable String version, @Nullable String versionPattern, ExecutionContext ctx) throws MavenDownloadingException { + String currentVersion = "0"; + if (gradleProject != null) { + GradleDependencyConfiguration gdc = gradleProject.getConfiguration(configuration); + if(gdc != null) { + Dependency requested = gdc.findRequestedDependency(ga.getGroupId(), ga.getArtifactId()); + if(requested != null) { + currentVersion = requested.getVersion(); + } + } + } return select( - new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), "0"), + new GroupArtifactVersion(ga.getGroupId(), ga.getArtifactId(), currentVersion), configuration, // we don't want to select the latest patch in the 0.x line... - "latest.patch".equalsIgnoreCase(version) ? "latest.release" : version, + "latest.patch".equalsIgnoreCase(version) && "0".equals(currentVersion) ? "latest.release" : version, versionPattern, ctx ); From 6d61f751cb6d2305ef439d36f97d7f8f5b28f1c0 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 17 Sep 2024 17:50:26 -0700 Subject: [PATCH 59/82] Get rid of any remaining possibility for NullPointerException when upgrading gradle dependency versions --- .../gradle/UpgradeDependencyVersion.java | 18 +++++---- .../maven/UpgradeDependencyVersionTest.java | 38 ------------------- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index 2bdf0a27197..671f45a89b8 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -53,7 +53,6 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static java.util.Objects.requireNonNull; @Value @EqualsAndHashCode(callSuper = false) @@ -226,8 +225,10 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) try { String resolvedVersion = new DependencyVersionSelector(metadataFailures, gradleProject, null) .select(new GroupArtifact(groupId, artifactId), m.getSimpleName(), newVersion, versionPattern, ctx); - acc.versionPropNameToGA.put(requireNonNull(versionVariableName), ga); - acc.gaToNewVersion.put(ga, requireNonNull(resolvedVersion)); + acc.versionPropNameToGA.put(versionVariableName, ga); + // It is fine for this value to be null, record it in the map to avoid future lookups + //noinspection DataFlowIssue + acc.gaToNewVersion.put(ga, resolvedVersion); } catch (MavenDownloadingException e) { acc.gaToNewVersion.put(ga, e); return m; @@ -458,7 +459,7 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution .toStringNotation(); return literal .withValue(newGav) - .withValueSource(requireNonNull(literal.getValueSource()).replace(gav, newGav)); + .withValueSource(literal.getValueSource() == null ? newGav : literal.getValueSource().replace(gav, newGav)); } catch (MavenDownloadingException e) { getCursor().putMessage(UPDATE_VERSION_ERROR_KEY, e); } @@ -481,11 +482,10 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution } J.Literal groupLiteral = (J.Literal) groupValue; J.Literal artifactLiteral = (J.Literal) artifactValue; - //noinspection DataFlowIssue - if (!dependencyMatcher.matches((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())) { + if (groupLiteral.getValue() == null || artifactLiteral.getValue() == null || !dependencyMatcher.matches((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())) { return m; } - Object scanResult = acc.gaToNewVersion.get(new GroupArtifact((String) requireNonNull(groupLiteral.getValue()), (String) artifactLiteral.getValue())); + Object scanResult = acc.gaToNewVersion.get(new GroupArtifact((String) groupLiteral.getValue(), (String) artifactLiteral.getValue())); if (scanResult instanceof Exception) { return Markup.warn(m, (Exception) scanResult); } @@ -513,7 +513,9 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution newArgs.add(depArgs.get(1)); newArgs.add(versionEntry.withValue( versionLiteral - .withValueSource(requireNonNull(versionLiteral.getValueSource()).replace(version, selectedVersion)) + .withValueSource(versionLiteral.getValueSource() == null ? + selectedVersion : + versionLiteral.getValueSource().replace(version, selectedVersion)) .withValue(selectedVersion))); newArgs.addAll(depArgs.subList(3, depArgs.size())); diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java index 19ef04d26e0..63b2e70bc43 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/UpgradeDependencyVersionTest.java @@ -577,11 +577,9 @@ void upgradePluginDependenciesOnProperty() { com.mycompany.app my-app 1 - 4.33.0 - @@ -605,11 +603,9 @@ void upgradePluginDependenciesOnProperty() { com.mycompany.app my-app 1 - 4.33.2 - @@ -1365,11 +1361,9 @@ void removesRedundantExplicitVersionsMatchingOldImport() { """ 4.0.0 - com.mycompany.app my-app 1 - @@ -1381,7 +1375,6 @@ void removesRedundantExplicitVersionsMatchingOldImport() { - org.junit.jupiter @@ -1394,11 +1387,9 @@ void removesRedundantExplicitVersionsMatchingOldImport() { """ 4.0.0 - com.mycompany.app my-app 1 - @@ -1410,7 +1401,6 @@ void removesRedundantExplicitVersionsMatchingOldImport() { - org.junit.jupiter @@ -1431,11 +1421,9 @@ void removesRedundantExplicitVersionsMatchingNewImport() { """ 4.0.0 - com.mycompany.app my-app 1 - @@ -1447,7 +1435,6 @@ void removesRedundantExplicitVersionsMatchingNewImport() { - org.junit.jupiter @@ -1460,11 +1447,9 @@ void removesRedundantExplicitVersionsMatchingNewImport() { """ 4.0.0 - com.mycompany.app my-app 1 - @@ -1476,7 +1461,6 @@ void removesRedundantExplicitVersionsMatchingNewImport() { - org.junit.jupiter @@ -1497,11 +1481,9 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { """ 4.0.0 - com.mycompany.app my-app 1 - @@ -1513,7 +1495,6 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { - org.junit.jupiter @@ -1526,11 +1507,9 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { """ 4.0.0 - com.mycompany.app my-app 1 - @@ -1542,7 +1521,6 @@ void keepsRedundantExplicitVersionsNotMatchingOldOrNewImport() { - org.junit.jupiter @@ -1571,7 +1549,6 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - @@ -1583,7 +1560,6 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { - com.jcraft @@ -1600,7 +1576,6 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - @@ -1612,7 +1587,6 @@ void dependencyWithExplicitVersionRemovedFromDepMgmt() { - com.jcraft @@ -1637,7 +1611,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - @@ -1649,7 +1622,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { - com.jcraft @@ -1665,7 +1637,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { org.sample sample 1.0.0 - @@ -1677,7 +1648,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmt() { - com.jcraft @@ -1702,7 +1672,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { org.sample sample 1.0.0 - @@ -1714,7 +1683,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { - com.jcraft @@ -1730,7 +1698,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { org.sample sample 1.0.0 - @@ -1742,7 +1709,6 @@ void dependencyWithoutExplicitVersionRemovedFromDepMgmtRetainSpecificVersion() { - com.jcraft @@ -1770,7 +1736,6 @@ void multipleRetainVersions() { org.sample sample 1.0.0 - @@ -1782,7 +1747,6 @@ void multipleRetainVersions() { - com.jcraft @@ -1802,7 +1766,6 @@ void multipleRetainVersions() { org.sample sample 1.0.0 - @@ -1814,7 +1777,6 @@ void multipleRetainVersions() { - com.jcraft From 75df3640eceda0a0c46f26b0d6d4f22ab1738b80 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 17 Sep 2024 18:47:29 -0700 Subject: [PATCH 60/82] One more tweak to avoid version related NPEs --- .../java/org/openrewrite/gradle/DependencyVersionSelector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java index 51328fc91e2..ab99113e377 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyVersionSelector.java @@ -75,7 +75,7 @@ public class DependencyVersionSelector { GradleDependencyConfiguration gdc = gradleProject.getConfiguration(configuration); if(gdc != null) { Dependency requested = gdc.findRequestedDependency(ga.getGroupId(), ga.getArtifactId()); - if(requested != null) { + if(requested != null && requested.getVersion() != null) { currentVersion = requested.getVersion(); } } From 77aa5f1fb2ab1403d04c6791dbadeb279ab10a87 Mon Sep 17 00:00:00 2001 From: Peter Streef Date: Wed, 18 Sep 2024 11:12:42 +0200 Subject: [PATCH 61/82] Use a `HEAD` request to check if a maven server is reachable (#4500) * Use a `HEAD` request to check if a maven server is reachable * fix more tests --- .../maven/internal/MavenPomDownloader.java | 5 +- .../openrewrite/maven/MavenParserTest.java | 8 ++- .../internal/MavenPomDownloaderTest.java | 66 +++++++++++-------- 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java index ba2eb96e64f..383241d0412 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java @@ -750,7 +750,7 @@ Collection distinctNormalizedRepositories( httpsUri += "/"; } - HttpSender.Request.Builder request = applyAuthenticationToRequest(repository, httpSender.get(httpsUri)); + HttpSender.Request.Builder request = applyAuthenticationToRequest(repository, httpSender.head(httpsUri)); MavenRepository normalized = null; try { sendRequest(request.build()); @@ -792,8 +792,7 @@ Collection distinctNormalizedRepositories( } } } - if (normalized == null && !(t instanceof HttpSenderResponseException && - ((HttpSenderResponseException) t).getBody().contains("Directory listing forbidden"))) { + if (normalized == null) { ctx.getResolutionListener().repositoryAccessFailed(repository.getUri(), t); } } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java index 72de19ebbb5..e2a30d2263f 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/MavenParserTest.java @@ -915,8 +915,9 @@ public MockResponse dispatch(RecordedRequest request) { it.getSecond().equals("Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes())))) { return resp.setResponseCode(401); } else { - //language=xml - resp.setBody(""" + if(!"HEAD".equalsIgnoreCase(request.getMethod())){ + //language=xml + resp.setBody(""" 4.0.0 @@ -926,7 +927,8 @@ public MockResponse dispatch(RecordedRequest request) { """ - ); + ); + } return resp.setResponseCode(200); } } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java index 2663320a74b..5423675b68b 100755 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java @@ -45,10 +45,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; +import static java.util.Collections.*; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -406,17 +403,24 @@ void usesAuthenticationIfRepositoryHasCredentials() { mockRepo.setDispatcher(new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest recordedRequest) { - return recordedRequest.getHeaders().get("Authorization") != null ? - new MockResponse().setResponseCode(200).setBody( - //language=xml - """ - - org.springframework.cloud - spring-cloud-dataflow-build - 2.10.0-SNAPSHOT - - """) : - new MockResponse().setResponseCode(401).setBody(""); + MockResponse response = new MockResponse(); + if (recordedRequest.getHeaders().get("Authorization") != null) { + response.setResponseCode(200); + if (!"HEAD".equalsIgnoreCase(recordedRequest.getMethod())) { + response.setBody( + //language=xml + """ + + org.springframework.cloud + spring-cloud-dataflow-build + 2.10.0-SNAPSHOT + + """); + } + } else { + response.setResponseCode(401); + } + return response; } }); mockRepo.start(); @@ -443,17 +447,21 @@ void doesNotUseAuthenticationIfCredentialsCannotBeResolved() { mockRepo.setDispatcher(new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest recordedRequest) { - return recordedRequest.getHeaders().get("Authorization") == null ? - new MockResponse().setResponseCode(200).setBody( - //language=xml - """ - - org.springframework.cloud - spring-cloud-dataflow-build - 2.10.0-SNAPSHOT - - """) : - new MockResponse().setResponseCode(401).setBody(""); + MockResponse response = new MockResponse(); + if (recordedRequest.getHeaders().get("Authorization") != null) { + response.setResponseCode(401); + } else if (recordedRequest.getMethod() == null || !recordedRequest.getMethod().equalsIgnoreCase("HEAD")) { + response.setBody( + //language=xml + """ + + org.springframework.cloud + spring-cloud-dataflow-build + 2.10.0-SNAPSHOT + + """); + } + return response; } }); mockRepo.start(); @@ -540,11 +548,11 @@ public MockResponse dispatch(RecordedRequest request) { """ 4.0.0 - + org.openrewrite.test foo 0.1.0-SNAPSHOT - + snapshot @@ -561,7 +569,7 @@ public MockResponse dispatch(RecordedRequest request) { http://%s:%d - + org.springframework.cloud From 117771d1570cebb09f63e09ce3a89ec2e807002e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 18 Sep 2024 13:03:50 +0200 Subject: [PATCH 62/82] Remove unused and not visible ParserTypeUtils --- .../java/org/openrewrite/test/ParserType.java | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 rewrite-test/src/main/java/org/openrewrite/test/ParserType.java diff --git a/rewrite-test/src/main/java/org/openrewrite/test/ParserType.java b/rewrite-test/src/main/java/org/openrewrite/test/ParserType.java deleted file mode 100644 index f850edc0c2b..00000000000 --- a/rewrite-test/src/main/java/org/openrewrite/test/ParserType.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - *

- * 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 - *

- * https://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. - */ -package org.openrewrite.test; - -import org.openrewrite.Parser; -import org.openrewrite.SourceFile; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -class ParserTypeUtils { - private ParserTypeUtils() { - } - - public static Class parserType(Parser parser) { - return parserType(parser.getClass()); - } - - private static Class parserType(Class parser) { - for (Type anInterface : parser.getGenericInterfaces()) { - if (anInterface instanceof Class) { - return parserType((Class) anInterface); - } - - if (anInterface instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) anInterface; - if (pt.getRawType().equals(Parser.class)) { - //noinspection unchecked - return (Class) pt.getActualTypeArguments()[0]; - } - } - } - - throw new IllegalArgumentException("Could not determine SourceFile type for this parser."); - } -} From 4e70fad4902e1dcb1cbf6db7292c632019908dae Mon Sep 17 00:00:00 2001 From: DidierLoiseau Date: Wed, 18 Sep 2024 18:03:57 +0200 Subject: [PATCH 63/82] RemoveRedundantDependencyVersions handles pluginManagement incorrectly (#4493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Incorrect pluginManagement handling from #3932 - version shouldn't be removed from plugin in pluginManagement when no parent - version should be removed from pluginManagement when matches parent - plugin should be removed from pluginManagement when nothing else is left * Restore methods to evaluate managed plugins separately * Finish Tim’s implementation * Apply formatter * Reduce duplication * Some tests with inherited plugins * Rename variable as suggested * Fix final unit test --------- Co-authored-by: Tim te Beek --- .../org/openrewrite/maven/MavenVisitor.java | 31 +- .../RemoveRedundantDependencyVersions.java | 97 ++++-- ...RemoveRedundantDependencyVersionsTest.java | 309 ++++++++++++++++++ 3 files changed, 404 insertions(+), 33 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java index 2d9d99b87c3..a6d658cd9ed 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java @@ -42,9 +42,9 @@ public class MavenVisitor

extends XmlVisitor

{ static final XPathMatcher PROFILE_MANAGED_DEPENDENCY_MATCHER = new XPathMatcher("/project/profiles/profile/dependencyManagement/dependencies/dependency"); static final XPathMatcher PROPERTY_MATCHER = new XPathMatcher("/project/properties/*"); static final XPathMatcher PLUGIN_MATCHER = new XPathMatcher("//plugins/plugin"); + static final XPathMatcher MANAGED_PLUGIN_MATCHER = new XPathMatcher("//pluginManagement/plugins/plugin"); static final XPathMatcher PARENT_MATCHER = new XPathMatcher("/project/parent"); - private transient Xml.@Nullable Document document; @Nullable @@ -221,6 +221,10 @@ public boolean isPluginTag(String groupId, @Nullable String artifactId) { return isPluginTag() && hasPluginGroupId(groupId) && hasPluginArtifactId(artifactId); } + public boolean isManagedPluginTag() { + return isTag("plugin") && MANAGED_PLUGIN_MATCHER.matches(getCursor()); + } + private boolean hasPluginGroupId(String groupId) { Xml.Tag tag = getCursor().getValue(); boolean isGroupIdFound = matchesGlob(tag.getChildValue("groupId").orElse("org.apache.maven.plugins"), groupId); @@ -388,16 +392,21 @@ public boolean isDependencyLikeTag() { } public @Nullable Plugin findPlugin(Xml.Tag tag) { - List plugins = getResolutionResult().getPom().getPlugins(); - if (plugins != null) { - for (Plugin resolvedPlugin : plugins) { - String reqGroup = resolvedPlugin.getGroupId(); - String reqVersion = resolvedPlugin.getVersion(); - if ((reqGroup == null || reqGroup.equals(tag.getChildValue("groupId").orElse(null))) && - resolvedPlugin.getArtifactId().equals(tag.getChildValue("artifactId").orElse(null)) && - (reqVersion == null || reqVersion.equals(tag.getChildValue("version").orElse(null)))) { - return resolvedPlugin; - } + return findPlugin(tag, getResolutionResult().getPom().getPlugins()); + } + + public @Nullable Plugin findManagedPlugin(Xml.Tag tag) { + return findPlugin(tag, getResolutionResult().getPom().getPluginManagement()); + } + + private static @Nullable Plugin findPlugin(Xml.Tag tag, List plugins) { + for (Plugin resolvedPlugin : plugins) { + String reqGroup = resolvedPlugin.getGroupId(); + String reqVersion = resolvedPlugin.getVersion(); + if ((reqGroup == null || reqGroup.equals(tag.getChildValue("groupId").orElse(null))) && + resolvedPlugin.getArtifactId().equals(tag.getChildValue("artifactId").orElse(null)) && + (reqVersion == null || reqVersion.equals(tag.getChildValue("version").orElse(null)))) { + return resolvedPlugin; } } return null; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java index c6c098b633a..4f4f8df299e 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java @@ -30,10 +30,7 @@ import org.openrewrite.semver.VersionComparator; import org.openrewrite.xml.tree.Xml; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import static org.openrewrite.internal.StringUtils.matchesGlob; @@ -168,7 +165,8 @@ public TreeVisitor getVisitor() { public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { Xml.Document d = super.visitDocument(document, ctx); if (d != document) { - d = (Xml.Document) new RemoveEmptyDependencyTags().visitNonNull(d, ctx); + d = (Xml.Document) new RemoveEmptyDependenciesTags().visitNonNull(d, ctx); + d = (Xml.Document) new RemoveEmptyPluginsTags().visitNonNull(d, ctx); if (!comparator.equals(Comparator.EQ)) { maybeUpdateModel(); } @@ -178,19 +176,11 @@ public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { @Override public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { - if (isDependencyTag() || isPluginTag()) { - if (isPluginTag()) { - Plugin p = findPlugin(tag); - if (p != null && matchesGroup(p) && matchesArtifact(p) && matchesVersion(p)) { - Xml.Tag version = tag.getChild("version").orElse(null); - return tag.withContent(ListUtils.map(tag.getContent(), c -> c == version ? null : c)); - } - } else { - ResolvedDependency d = findDependency(tag); - if (d != null && matchesGroup(d) && matchesArtifact(d) && matchesVersion(d) && isNotExcepted(d)) { - Xml.Tag version = tag.getChild("version").orElse(null); - return tag.withContent(ListUtils.map(tag.getContent(), c -> c == version ? null : c)); - } + if (isDependencyTag()) { + ResolvedDependency d = findDependency(tag); + if (d != null && matchesGroup(d) && matchesArtifact(d) && matchesVersion(d) && isNotExcepted(d)) { + Xml.Tag version = tag.getChild("version").orElse(null); + return tag.withContent(ListUtils.map(tag.getContent(), c -> c == version ? null : c)); } } else if (isManagedDependencyTag()) { ResolvedManagedDependency managed = findManagedDependency(tag); @@ -201,6 +191,30 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { //noinspection DataFlowIssue return null; } + } else if (isPluginTag()) { + if (isManagedPluginTag()) { + Xml.Tag version = tag.getChild("version").orElse(null); + if (version == null) { + // version is not managed here + return tag; + } + Plugin p = findManagedPlugin(tag); + if (p != null && matchesGroup(p) && matchesArtifact(p) && matchesManagedVersion(p, ctx)) { + Set gavTags = new HashSet<>(Arrays.asList("groupId", "artifactId", "version")); + if (tag.getChildren().stream().allMatch(t -> gavTags.contains(t.getName()))) { + // only the version was specified for this managed plugin, so no need to keep the declaration + return null; + } + // some other element is also declared (executions, configuration, dependencies…), so just remove the version + return tag.withContent(ListUtils.map(tag.getContent(), c -> c == version ? null : c)); + } + } else { + Plugin p = findPlugin(tag); + if (p != null && matchesGroup(p) && matchesArtifact(p) && matchesVersion(p)) { + Xml.Tag version = tag.getChild("version").orElse(null); + return tag.withContent(ListUtils.map(tag.getContent(), c -> c == version ? null : c)); + } + } } return super.visitTag(tag, ctx); } @@ -278,6 +292,33 @@ private boolean matchesVersion(Plugin p) { return matchesComparator(managedVersion, p.getVersion()); } + + /** + * This compares a managed plugin version to the version which would be used if only the parent's + * plugin management were in effect. This enables detection of managed plugin versions which + * could be left to the parent. + */ + private boolean matchesManagedVersion(Plugin p, ExecutionContext ctx) { + MavenResolutionResult mrr = getResolutionResult(); + if (p.getVersion() == null || mrr.getPom().getRequested().getParent() == null) { + return false; + } + try { + GroupArtifactVersion parentGav = mrr.getPom().getRequested().getParent().getGav(); + MavenPomDownloader mpd = new MavenPomDownloader(mrr.getProjectPoms(), ctx, mrr.getMavenSettings(), mrr.getActiveProfiles()); + ResolvedPom parentPom = mpd.download(parentGav, null, mrr.getPom(), mrr.getPom().getRepositories()) + .resolve(Collections.emptyList(), mpd, ctx); + return parentPom.getPluginManagement().stream() + .filter(plugin -> plugin.getGroupId().equals(p.getGroupId()) && plugin.getArtifactId().equals(p.getArtifactId())) + .findFirst() + .map(Plugin::getVersion) + .map(versionAccordingToParent -> matchesComparator(parentPom.getValue(versionAccordingToParent), p.getVersion())) + .orElse(false); + } catch (Exception e) { + return false; + } + } + private boolean matchesComparator(@Nullable String managedVersion, String requestedVersion) { if (managedVersion == null) { return false; @@ -313,8 +354,8 @@ private boolean isNotExcepted(ResolvedDependency d) { final String[] split = gav.split(":"); final String exceptedGroupId = split[0]; final String exceptedArtifactId = split[1]; - if (matchesGlob(d.getGroupId(), exceptedGroupId) - && matchesGlob(d.getArtifactId(), exceptedArtifactId)) { + if (matchesGlob(d.getGroupId(), exceptedGroupId) && + matchesGlob(d.getArtifactId(), exceptedArtifactId)) { return false; } } @@ -324,7 +365,7 @@ && matchesGlob(d.getArtifactId(), exceptedArtifactId)) { } private static @Nullable String getManagedPluginVersion(ResolvedPom resolvedPom, @Nullable String groupId, String artifactId) { - for (Plugin p : resolvedPom.getPluginManagement()) { + for (Plugin p : ListUtils.concatAll(resolvedPom.getPluginManagement(), resolvedPom.getRequested().getPluginManagement())) { if (Objects.equals( Optional.ofNullable(p.getGroupId()).orElse("org.apache.maven.plugins"), Optional.ofNullable(groupId).orElse("org.apache.maven.plugins")) && @@ -335,7 +376,7 @@ && matchesGlob(d.getArtifactId(), exceptedArtifactId)) { return null; } - private static class RemoveEmptyDependencyTags extends MavenIsoVisitor { + private static class RemoveEmptyDependenciesTags extends MavenIsoVisitor { @Override public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { Xml.Tag t = super.visitTag(tag, ctx); @@ -346,4 +387,16 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { return t; } } + + private static class RemoveEmptyPluginsTags extends MavenIsoVisitor { + @Override + public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { + Xml.Tag t = super.visitTag(tag, ctx); + if (("pluginManagement".equals(t.getName()) || "plugins".equals(t.getName())) && (t.getContent() == null || t.getContent().isEmpty())) { + //noinspection DataFlowIssue + return null; + } + return t; + } + } } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java index 3fa8e1c2328..073d136b347 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java @@ -1115,6 +1115,126 @@ void unmanagedDependencyAnyMatch() { ); } + @Test + void removeRedundantVersionsFromManagedPlugins() { + rewriteRun( + pomXml( + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + """, + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + + pl.project13.maven + git-commit-id-plugin + + + + + """ + ) + ); + } + + @Test + void removeRedundantVersionsFromManagedButNonInheritedPlugins() { + rewriteRun( + pomXml( + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + false + + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + """, + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + false + + + + + + pl.project13.maven + git-commit-id-plugin + + + + + """ + ) + ); + } + @Test @Issue("https://github.com/openrewrite/rewrite/issues/3932") void removeRedundantVersionsFromPluginsManagedByParent() { @@ -1170,6 +1290,195 @@ void removeRedundantVersionsFromPluginsManagedByParent() { ); } + @Test + void keepPluginIfParentInheritanceDisabled() { + rewriteRun( + pomXml( + """ + + org.example + parent + 1.0-SNAPSHOT + + child-with-pluginManagement + child-with-plugin-execution + + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + false + + + + + + """ + ), + mavenProject("child-with-pluginManagement", + pomXml( + """ + + + org.example + parent + 1.0-SNAPSHOT + + child-with-pluginManagement + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + + """ + ) + ), + mavenProject("child-with-plugin-execution", + pomXml( + """ + + + org.example + parent + 1.0-SNAPSHOT + + child-with-plugin-execution + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + """ + ) + ) + ); + } + + @Test + void removeRedundantManagedVersionFromPluginsManagedByParentAndKeepOtherElements() { + rewriteRun( + spec -> spec.recipe(new RemoveRedundantDependencyVersions(null, null, RemoveRedundantDependencyVersions.Comparator.GTE, null)), + pomXml( + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + + + """, + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + + + + + pl.project13.maven + git-commit-id-plugin + + + + + + + """ + ) + ); + } + + @Test + void removeRedundantManagedPluginWhenPluginManagedByParentAndNoOtherElement() { + rewriteRun( + spec -> spec.recipe(new RemoveRedundantDependencyVersions(null, null, RemoveRedundantDependencyVersions.Comparator.GTE, null)), + pomXml( + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + + + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + + + + """, + """ + + 4.0.0 + org.sample + sample + 1.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + + + + """ + ) + ); + } + @Test @Issue("https://github.com/openrewrite/rewrite/issues/3932") void noChangesIfManagedPluginVersionDoesNotMatch() { From d8be41b15e7df08d7244820986c2f1cee8b2c2ae Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Wed, 18 Sep 2024 19:25:13 -0700 Subject: [PATCH 64/82] Add handling of comment indentation to yaml AutoFormat --- .../openrewrite/yaml/MergeYamlVisitor.java | 2 +- .../yaml/format/AutoFormatVisitor.java | 19 +++--- .../yaml/format/IndentsVisitor.java | 44 +++++++++---- .../org/openrewrite/yaml/MergeYamlTest.java | 2 +- .../openrewrite/yaml/format/IndentsTest.java | 64 +++++++++++++++++++ 5 files changed, 108 insertions(+), 23 deletions(-) diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/MergeYamlVisitor.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/MergeYamlVisitor.java index 03d7bf52917..05e1eb43316 100644 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/MergeYamlVisitor.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/MergeYamlVisitor.java @@ -129,7 +129,7 @@ private Yaml.Mapping mergeMapping(Yaml.Mapping m1, Yaml.Mapping m2, P p, Cursor } } if (shouldAutoFormat) { - return autoFormat(incomingEntry, p, cursor); + incomingEntry = autoFormat(incomingEntry, p, cursor); } return incomingEntry; })); diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/AutoFormatVisitor.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/AutoFormatVisitor.java index 5ddff39541d..6d64bda1f25 100644 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/AutoFormatVisitor.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/AutoFormatVisitor.java @@ -36,41 +36,40 @@ public AutoFormatVisitor(@Nullable Tree stopAfter) { } @Override - public @Nullable Yaml preVisit(Yaml tree, P p) { + public Yaml preVisit(Yaml tree, P p) { stopAfterPreVisit(); Yaml.Documents docs = getCursor().firstEnclosingOrThrow(Yaml.Documents.class); Cursor cursor = getCursor().getParentOrThrow(); - Yaml y = new NormalizeFormatVisitor<>(stopAfter).visit(tree, p, cursor.fork()); + Yaml y = new NormalizeFormatVisitor<>(stopAfter).visitNonNull(tree, p, cursor.fork()); - y = new MinimumViableSpacingVisitor<>(stopAfter).visit(y, p, cursor.fork()); + y = new MinimumViableSpacingVisitor<>(stopAfter).visitNonNull(y, p, cursor.fork()); y = new IndentsVisitor<>(Optional.ofNullable(docs.getStyle(IndentsStyle.class)) .orElse(Autodetect.tabsAndIndents(docs, YamlDefaultStyles.indents())), stopAfter) - .visit(y, p, cursor.fork()); + .visitNonNull(y, p, cursor.fork()); y = new NormalizeLineBreaksVisitor<>(Optional.ofNullable(docs.getStyle(GeneralFormatStyle.class)) .orElse(Autodetect.generalFormat(docs)), stopAfter) - .visit(y, p, cursor.fork()); + .visitNonNull(y, p, cursor.fork()); return y; } @Override public Yaml.Documents visitDocuments(Yaml.Documents documents, P p) { - Yaml.Documents y = (Yaml.Documents) new NormalizeFormatVisitor<>(stopAfter).visit(documents, p); + Yaml.Documents y = (Yaml.Documents) new NormalizeFormatVisitor<>(stopAfter).visitNonNull(documents, p); - y = (Yaml.Documents) new MinimumViableSpacingVisitor<>(stopAfter).visit(y, p); + y = (Yaml.Documents) new MinimumViableSpacingVisitor<>(stopAfter).visitNonNull(y, p); y = (Yaml.Documents) new IndentsVisitor<>(Optional.ofNullable(documents.getStyle(IndentsStyle.class)) .orElse(Autodetect.tabsAndIndents(y, YamlDefaultStyles.indents())), stopAfter) - .visit(documents, p); + .visitNonNull(documents, p); y = (Yaml.Documents) new NormalizeLineBreaksVisitor<>(Optional.ofNullable(documents.getStyle(GeneralFormatStyle.class)) .orElse(Autodetect.generalFormat(y)), stopAfter) - .visit(documents, p); + .visitNonNull(documents, p); - assert y != null; return y; } diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/IndentsVisitor.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/IndentsVisitor.java index 7d7bbd2c079..63dd01d20f8 100755 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/IndentsVisitor.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/format/IndentsVisitor.java @@ -18,6 +18,7 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.Tree; +import org.openrewrite.internal.StringUtils; import org.openrewrite.yaml.YamlIsoVisitor; import org.openrewrite.yaml.style.IndentsStyle; import org.openrewrite.yaml.tree.Yaml; @@ -73,19 +74,24 @@ public IndentsVisitor(IndentsStyle style, @Nullable Tree stopAfter) { getCursor().getParentOrThrow().putMessage("sequenceEntryIndent", indent); // the +1 is for the '-' character - getCursor().getParentOrThrow().putMessage("lastIndent", indent + - firstIndent(((Yaml.Sequence.Entry) y).getBlock()).length() + 1); + getCursor().getParentOrThrow().putMessage("lastIndent", + indent + firstIndent(((Yaml.Sequence.Entry) y).getBlock()).length() + 1); } else if (y instanceof Yaml.Mapping.Entry) { y = y.withPrefix(indentTo(y.getPrefix(), indent + style.getIndentSize())); getCursor().putMessage("lastIndent", indent + style.getIndentSize()); + } else if (y instanceof Yaml.Document) { + y = y.withPrefix(indentComments(y.getPrefix(), 0)); + } + } else if (y instanceof Yaml.Mapping.Entry) { + if (getCursor().getParentOrThrow(2).getValue() instanceof Yaml.Sequence.Entry) { + // this is a mapping entry that begins a sequence entry and anything below it should be indented further to the right now, e.g.: + // + // - key: + // value + getCursor().putMessage("lastIndent", indent + style.getIndentSize()); + } else { + y = y.withPrefix(indentComments(y.getPrefix(), indent)); } - } else if (y instanceof Yaml.Mapping.Entry && - getCursor().getParentOrThrow(2).getValue() instanceof Yaml.Sequence.Entry) { - // this is a mapping entry that begins a sequence entry and anything below it should be indented further to the right now, e.g.: - // - // - key: - // value - getCursor().putMessage("lastIndent", indent + style.getIndentSize()); } return y; } @@ -117,7 +123,7 @@ private String indentTo(String prefix, int column) { } int indent = findIndent(prefix); - + prefix = indentComments(prefix, indent); if (indent != column) { int shift = column - indent; prefix = indent(prefix, shift); @@ -126,6 +132,21 @@ private String indentTo(String prefix, int column) { return prefix; } + private String indentComments(String prefix, int indent) { + // If the prefix contains a newline followed by a comment ensure the comment begins at the indentation column + if (prefix.contains("#")) { + String reindentedComments = prefix.replaceAll("\n\\s*#", "\n" + StringUtils.repeat(" ", indent) + "#"); + // If a document begins with a comment it might not have a newline before it + if (getCursor().getValue() instanceof Yaml.Document) { + reindentedComments = prefix.replaceFirst("^\\s*#", "#"); + } + if (!reindentedComments.equals(prefix)) { + prefix = reindentedComments; + } + } + return prefix; + } + private String indent(String whitespace, int shift) { StringBuilder newWhitespace = new StringBuilder(whitespace); shift(newWhitespace, shift); @@ -154,12 +175,13 @@ private int findIndent(String prefix) { } private String firstIndent(Yaml yaml) { - AtomicReference indent = new AtomicReference<>(); + AtomicReference<@Nullable String> indent = new AtomicReference<>(); new YamlIsoVisitor>() { @Override public @Nullable Yaml visit(@Nullable Tree tree, AtomicReference indent) { Yaml y = (Yaml) tree; + //noinspection ConstantValue if (indent.get() != null) { return y; } diff --git a/rewrite-yaml/src/test/java/org/openrewrite/yaml/MergeYamlTest.java b/rewrite-yaml/src/test/java/org/openrewrite/yaml/MergeYamlTest.java index fc122e6394c..59418ad5c34 100644 --- a/rewrite-yaml/src/test/java/org/openrewrite/yaml/MergeYamlTest.java +++ b/rewrite-yaml/src/test/java/org/openrewrite/yaml/MergeYamlTest.java @@ -1145,7 +1145,7 @@ void comment() { //language=yaml """ - # new stuff + # new stuff new-property: value """, false, diff --git a/rewrite-yaml/src/test/java/org/openrewrite/yaml/format/IndentsTest.java b/rewrite-yaml/src/test/java/org/openrewrite/yaml/format/IndentsTest.java index 9046005ff41..7b34716920b 100644 --- a/rewrite-yaml/src/test/java/org/openrewrite/yaml/format/IndentsTest.java +++ b/rewrite-yaml/src/test/java/org/openrewrite/yaml/format/IndentsTest.java @@ -25,6 +25,7 @@ import static org.openrewrite.test.RewriteTest.toRecipe; import static org.openrewrite.yaml.Assertions.yaml; +@SuppressWarnings("KubernetesUnknownResourcesInspection") class IndentsTest implements RewriteTest { @Override @@ -115,4 +116,67 @@ void maintainIndentSpacingOnMixedTypeSequences() { ) ); } + + @Test + void indentSequenceComments() { + rewriteRun( + yaml(""" + key: + # under-indented + # over-indented + - a + """, + """ + key: + # under-indented + # over-indented + - a + """ + ) + ); + } + + @Test + void indentMappingComments() { + rewriteRun( + yaml(""" + key: # no change + # under-indented + # over-indented + a : # no change + # under-indented + # over-indented + b : c + """, + """ + key: # no change + # under-indented + # over-indented + a : # no change + # under-indented + # over-indented + b : c + """ + ) + ); + } + + @Test + void indentRootComments() { + rewriteRun( + yaml(""" + # over-indented 1 + key: value # no change + # over-indented 2 + key2: value2 + """, + """ + # over-indented 1 + key: value # no change + # over-indented 2 + key2: value2 + """ + ) + ); + } } From b6577f94de28eaea703901a0f353c1ecc4ec713d Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 19 Sep 2024 10:54:50 -0700 Subject: [PATCH 65/82] Nothing extends Document --- rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java index 22f1ffaf1a4..a92c0370e54 100755 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/tree/Yaml.java @@ -90,7 +90,7 @@ public SourceFile withCharset(Charset charset) { return withCharsetName(charset.name()); } - List documents; + List documents; @Override public

Yaml acceptYaml(YamlVisitor

v, P p) { From 63252cb1abc79256813413b8d6c331e426ff9415 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 19 Sep 2024 12:13:35 -0700 Subject: [PATCH 66/82] Mention that system scope can be searched within. --- .../java/org/openrewrite/maven/search/DependencyInsight.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/search/DependencyInsight.java b/rewrite-maven/src/main/java/org/openrewrite/maven/search/DependencyInsight.java index b4dc4deeebb..c3373a258f6 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/search/DependencyInsight.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/search/DependencyInsight.java @@ -56,7 +56,7 @@ public class DependencyInsight extends Recipe { @Option(displayName = "Scope", description = "Match dependencies with the specified scope. All scopes are searched by default.", - valid = {"compile", "test", "runtime", "provided"}, + valid = {"compile", "test", "runtime", "provided", "system"}, example = "compile", required = false) @Nullable From ee52aca10ffacf9a6c66a3c99599e90533ec5cf4 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 19 Sep 2024 12:37:34 -0700 Subject: [PATCH 67/82] Make method pattern optional --- .../java/org/openrewrite/java/trait/MethodAccess.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rewrite-java/src/main/java/org/openrewrite/java/trait/MethodAccess.java b/rewrite-java/src/main/java/org/openrewrite/java/trait/MethodAccess.java index d5087341ab2..1ab5e98790b 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/trait/MethodAccess.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/trait/MethodAccess.java @@ -41,9 +41,14 @@ public class MethodAccess implements Trait { @RequiredArgsConstructor public static class Matcher extends SimpleTraitMatcher { + @Nullable private final MethodMatcher methodMatcher; private Predicate<@Nullable JavaType> returnsTest = m -> true; + public Matcher() { + methodMatcher = null; + } + public Matcher(String methodPattern) { this(new MethodMatcher(methodPattern)); } @@ -87,9 +92,7 @@ public J visitMemberReference(J.MemberReference memberRef, P p) { Object value = cursor.getValue(); JavaType.Method methodType = ((MethodCall) value).getMethodType(); JavaType returnType = methodType == null ? null : methodType.getReturnType(); - - return methodMatcher.matches(((Expression) value)) && - returnsTest.test(returnType) ? + return returnsTest.test(returnType) && (methodMatcher == null || methodMatcher.matches((Expression) value)) ? new MethodAccess(cursor) : null; } From bb228bfed2ddd259029ac538ee73bd61900e1375 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 19 Sep 2024 22:14:08 +0200 Subject: [PATCH 68/82] Add declarative Gradle upgrade recipes (#4502) * Add declarative Gradle upgrade recipes * Apply naming convention and chain recipes * Apply review comment --------- Co-authored-by: Shannon Pamperl --- .../resources/META-INF/rewrite/gradle-5.yml | 24 +++++++++ .../resources/META-INF/rewrite/gradle-6.yml | 51 +++++++++++++++++++ .../resources/META-INF/rewrite/gradle-7.yml | 30 +++++++++++ .../resources/META-INF/rewrite/gradle-8.yml | 34 +++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-5.yml create mode 100644 rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-6.yml create mode 100644 rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-7.yml create mode 100644 rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-8.yml diff --git a/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-5.yml b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-5.yml new file mode 100644 index 00000000000..a392d98c130 --- /dev/null +++ b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-5.yml @@ -0,0 +1,24 @@ +# +# Copyright 2024 the original author or authors. +#

+# 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 +#

+# https://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. +# +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.gradle.MigrateToGradle5 +displayName: Migrate to Gradle 5 from Gradle 4 +description: Migrate to version 5.x. See the Gradle upgrade guide from [version 4.x to 5.0](https://docs.gradle.org/current/userguide/upgrading_version_4.html) for more information. +recipeList: + - org.openrewrite.gradle.UpdateGradleWrapper: + version: 5.x + addIfMissing: false diff --git a/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-6.yml b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-6.yml new file mode 100644 index 00000000000..2dcc3df7dc7 --- /dev/null +++ b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-6.yml @@ -0,0 +1,51 @@ +# +# Copyright 2024 the original author or authors. +#

+# 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 +#

+# https://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. +# +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.gradle.MigrateToGradle6 +displayName: Migrate to Gradle 6 from Gradle 5 +description: Migrate to version 6.x. See the Gradle upgrade guide from [version 5.x to 6.0](https://docs.gradle.org/current/userguide/upgrading_version_5.html) for more information. +recipeList: + - org.openrewrite.gradle.MigrateToGradle5 + - org.openrewrite.gradle.UpdateGradleWrapper: + version: 6.x + addIfMissing: false + # We can't easily convert `compile` to either `api` or `implementation`, because transitive requirements are unclear + # https://github.com/openrewrite/rewrite/issues/4194 + - org.openrewrite.gradle.ChangeDependencyConfiguration: + groupId: "*" + artifactId: "*" + configuration: runtime + newConfiguration: runtimeOnly + - org.openrewrite.gradle.ChangeDependencyConfiguration: + groupId: "*" + artifactId: "*" + configuration: testCompile + newConfiguration: testImplementation + - org.openrewrite.gradle.ChangeDependencyConfiguration: + groupId: "*" + artifactId: "*" + configuration: testRuntime + newConfiguration: testRuntimeOnly + + # https://github.com/gradle/gradle/blob/v5.6.4/subprojects/core/src/main/java/org/gradle/api/internal/FeaturePreviews.java + # https://github.com/gradle/gradle/blob/v6.9.4/subprojects/core/src/main/java/org/gradle/api/internal/FeaturePreviews.java + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: IMPROVED_POM_SUPPORT + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: STABLE_PUBLISHING + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: INCREMENTAL_ARTIFACT_TRANSFORMATIONS diff --git a/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-7.yml b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-7.yml new file mode 100644 index 00000000000..8e7dade9fa4 --- /dev/null +++ b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-7.yml @@ -0,0 +1,30 @@ +# +# Copyright 2024 the original author or authors. +#

+# 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 +#

+# https://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. +# +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.gradle.MigrateToGradle7 +displayName: Migrate to Gradle 7 from Gradle 6 +description: Migrate to version 7.x. See the Gradle upgrade guide from [version 6.x to 7.0](https://docs.gradle.org/current/userguide/upgrading_version_6.html) for more information. +recipeList: + - org.openrewrite.gradle.MigrateToGradle6 + - org.openrewrite.gradle.UpdateGradleWrapper: + version: 7.x + addIfMissing: false + + # https://github.com/gradle/gradle/blob/v6.9.4/subprojects/core/src/main/java/org/gradle/api/internal/FeaturePreviews.java + # https://github.com/gradle/gradle/blob/v7.6.4/subprojects/core/src/main/java/org/gradle/api/internal/FeaturePreviews.java + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: GRADLE_METADATA diff --git a/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-8.yml b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-8.yml new file mode 100644 index 00000000000..9babdf64095 --- /dev/null +++ b/rewrite-gradle/src/main/resources/META-INF/rewrite/gradle-8.yml @@ -0,0 +1,34 @@ +# +# Copyright 2024 the original author or authors. +#

+# 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 +#

+# https://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. +# +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.gradle.MigrateToGradle8 +displayName: Migrate to Gradle 8 from Gradle 7 +description: Migrate to version 8.x. See the Gradle upgrade guide from [version 7.x to 8.0](https://docs.gradle.org/current/userguide/upgrading_version_7.html) and [version 8.x to latest](https://docs.gradle.org/current/userguide/upgrading_version_8.html) for more information. +recipeList: + - org.openrewrite.gradle.MigrateToGradle7 + - org.openrewrite.gradle.UpdateGradleWrapper: + version: 8.x + addIfMissing: false + + # https://github.com/gradle/gradle/blob/v7.6.4/subprojects/core/src/main/java/org/gradle/api/internal/FeaturePreviews.java + # https://github.com/gradle/gradle/blob/v8.10.1/subprojects/core/src/main/java/org/gradle/api/internal/FeaturePreviews.java + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: ONE_LOCKFILE_PER_PROJECT + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: VERSION_ORDERING_V2 + - org.openrewrite.gradle.RemoveEnableFeaturePreview: + previewFeatureName: VERSION_CATALOGS From 956fa7a820f1a520499165d5a2d1126033d12791 Mon Sep 17 00:00:00 2001 From: Leanne Kerford Date: Thu, 19 Sep 2024 15:54:40 -0700 Subject: [PATCH 69/82] Fixing duplicate search markers in javaDocs (#4503) When running the "FindTypes" recipe we are getting duplicate search markers being added to the result. We should only be printing one. --- .../java/search/FindTypesTest.java | 32 +++++++++++++++++++ .../org/openrewrite/java/JavadocPrinter.java | 12 ------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindTypesTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindTypesTest.java index 5db9f118a71..55f4d06de00 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindTypesTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/search/FindTypesTest.java @@ -425,4 +425,36 @@ class B { java(a1) ); } + + @Test + void javadocComment() { + + rewriteRun( + spec -> spec.recipe(new FindTypes("java.lang.String", true)), + java( + """ + public class A { + /** + * JavaDoc comment with {{@link String#trim()}} + * JavaDoc comment with String#trim() + */ + public static String replaceFoo(String string) { + return string.replaceAll("foo", "bar"); + } + } + """, + """ + public class A { + /** + * JavaDoc comment with {{@link ~~>String#trim()}} + * JavaDoc comment with String#trim() + */ + public static /*~~>*/String replaceFoo(/*~~>*/String string) { + return string.replaceAll("foo", "bar"); + } + } + """ + ) + ); + } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/JavadocPrinter.java b/rewrite-java/src/main/java/org/openrewrite/java/JavadocPrinter.java index 01c35f7a801..320f1cd7803 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/JavadocPrinter.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/JavadocPrinter.java @@ -464,18 +464,6 @@ public Space visitSpace(Space space, Space.Location loc, PrintOutputCapture

p return space; } - @Override - public M visitMarker(Marker marker, PrintOutputCapture

p) { - if (marker instanceof SearchResult) { - String description = ((SearchResult) marker).getDescription(); - p.append("~~") - .append(description == null ? "" : "(" + description + ")~~") - .append(">"); - } - //noinspection unchecked - return (M) marker; - } - private void visitLineBreak(Javadoc.LineBreak lineBreak, PrintOutputCapture

p) { beforeSyntax(Space.EMPTY, lineBreak.getMarkers(), null, p); p.append(lineBreak.getMargin()); From 1ed522465adf529a2fdfdb84eff31f0eef8de0cf Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 20 Sep 2024 11:08:47 +0000 Subject: [PATCH 70/82] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../test/java/org/openrewrite/java/OrderImportsTest.java | 6 ++++-- .../src/main/java/org/openrewrite/json/JsonParser.java | 1 - .../java/org/openrewrite/maven/AddRuntimeConfigTest.java | 6 +++++- .../openrewrite/maven/internal/MavenPomDownloaderTest.java | 5 ++++- .../src/main/java/org/openrewrite/xml/XmlParser.java | 1 - 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java index 85e6f79bfd7..938134b5de7 100755 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/OrderImportsTest.java @@ -22,12 +22,14 @@ import org.openrewrite.style.NamedStyles; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import org.openrewrite.test.SourceSpec; import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; import static org.openrewrite.Tree.randomId; -import static org.openrewrite.java.Assertions.*; +import static org.openrewrite.java.Assertions.addTypesToSourceSet; +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.srcMainJava; +import static org.openrewrite.java.Assertions.version; class OrderImportsTest implements RewriteTest { diff --git a/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java b/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java index 7f654cd33e9..98c43ede841 100755 --- a/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java +++ b/rewrite-json/src/main/java/org/openrewrite/json/JsonParser.java @@ -22,7 +22,6 @@ import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Parser; import org.openrewrite.SourceFile; -import org.openrewrite.Parser; import org.openrewrite.json.internal.JsonParserVisitor; import org.openrewrite.json.internal.grammar.JSON5Lexer; import org.openrewrite.json.internal.grammar.JSON5Parser; diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/AddRuntimeConfigTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/AddRuntimeConfigTest.java index 983e32f4d7c..f73a1ea5f7e 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/AddRuntimeConfigTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/AddRuntimeConfigTest.java @@ -24,7 +24,11 @@ import org.openrewrite.test.RewriteTest; import org.openrewrite.test.SourceSpecs; -import static org.openrewrite.maven.AddRuntimeConfig.*; +import static org.openrewrite.maven.AddRuntimeConfig.JVM_CONFIG_FILENAME; +import static org.openrewrite.maven.AddRuntimeConfig.JVM_CONFIG_PATH; +import static org.openrewrite.maven.AddRuntimeConfig.MAVEN_CONFIG_FILENAME; +import static org.openrewrite.maven.AddRuntimeConfig.MAVEN_CONFIG_PATH; +import static org.openrewrite.maven.AddRuntimeConfig.Separator; import static org.openrewrite.maven.Assertions.pomXml; import static org.openrewrite.test.SourceSpecs.text; diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java index 5423675b68b..e1e0a770709 100755 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenPomDownloaderTest.java @@ -45,7 +45,10 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import static java.util.Collections.*; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java index 098a5279fc0..cca7af905b5 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java @@ -22,7 +22,6 @@ import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Parser; import org.openrewrite.SourceFile; -import org.openrewrite.Parser; import org.openrewrite.internal.EncodingDetectingInputStream; import org.openrewrite.tree.ParseError; import org.openrewrite.tree.ParsingEventListener; From 7bc2966841e6f694df9e6030305333d9710e83ee Mon Sep 17 00:00:00 2001 From: DidierLoiseau Date: Fri, 20 Sep 2024 17:11:43 +0200 Subject: [PATCH 71/82] Creating "OR" preconditions instead of "AND" (#4003) does not actually work (#4505) * Added tests for declarative recipes as preconditions Includes the example from the documentation. * Allow declarative recipes to be used as preconditions * Remove arguments to `toArray` * Remove DeclarativeRecipe specificity * Improve formatting --------- Co-authored-by: Tim te Beek --- .../openrewrite/config/DeclarativeRecipe.java | 12 +++- .../config/DeclarativeRecipeTest.java | 59 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java b/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java index 150575a3217..0ecfab865fd 100644 --- a/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java +++ b/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java @@ -270,7 +270,7 @@ public final List getRecipeList() { getName() + " declares the ScanningRecipe " + precondition.getName() + " as a precondition." + "ScanningRecipe cannot be used as Preconditions."); } - andPreconditions.add(precondition::getVisitor); + andPreconditions.add(() -> orVisitors(precondition)); } PreconditionBellwether bellwether = new PreconditionBellwether(Preconditions.and(andPreconditions.toArray(new Supplier[]{}))); List recipeListWithBellwether = new ArrayList<>(recipeList.size() + 1); @@ -279,6 +279,16 @@ public final List getRecipeList() { return recipeListWithBellwether; } + private static TreeVisitor orVisitors(Recipe recipe) { + List> conditions = new ArrayList<>(); + conditions.add(recipe.getVisitor()); + for (Recipe r : recipe.getRecipeList()) { + conditions.add(orVisitors(r)); + } + //noinspection unchecked + return Preconditions.or(conditions.toArray(new TreeVisitor[0])); + } + private static boolean isScanningRecipe(Recipe recipe) { if (recipe instanceof ScanningRecipe) { return true; diff --git a/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java b/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java index a70b9c7701d..22f8f6ee263 100644 --- a/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java +++ b/rewrite-core/src/test/java/org/openrewrite/config/DeclarativeRecipeTest.java @@ -174,6 +174,65 @@ void yamlPrecondition() { ); } + @Test + void yamlDeclarativeRecipeAsPrecondition() { + rewriteRun( + spec -> spec.recipeFromYaml( + """ + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.PreconditionTest + description: Test. + preconditions: + - org.openrewrite.DeclarativePrecondition + recipeList: + - org.openrewrite.text.ChangeText: + toText: 3 + --- + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.DeclarativePrecondition + recipeList: + - org.openrewrite.text.Find: + find: 1 + """, + "org.openrewrite.PreconditionTest" + ), + text("1", "3"), + text("2") + ); + } + + @Test + void orPreconditions() { + // As documented https://docs.openrewrite.org/reference/yaml-format-reference#creating-or-preconditions-instead-of-and + rewriteRun( + spec -> spec.recipeFromYaml( + """ + type: specs.openrewrite.org/v1beta/recipe + name: org.sample.DoSomething + description: Test. + preconditions: + - org.sample.FindAnyJson + recipeList: + - org.openrewrite.text.ChangeText: + toText: 2 + --- + type: specs.openrewrite.org/v1beta/recipe + name: org.sample.FindAnyJson + recipeList: + - org.openrewrite.FindSourceFiles: + filePattern: "**/my.json" + - org.openrewrite.FindSourceFiles: + filePattern: "**/your.json" + - org.openrewrite.FindSourceFiles: + filePattern: "**/our.json" + """, + "org.sample.DoSomething" + ), + text("1", "2", spec -> spec.path("a/my.json")), + text("a", spec -> spec.path("a/not-my.json")) + ); + } + @Test void yamlPreconditionWithScanningRecipe() { rewriteRun( From cce1dcf94d8e218674e77194399976c689ac4f48 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 20 Sep 2024 17:29:50 +0200 Subject: [PATCH 72/82] Prevent ArrayList and array creation in DeclarativeRecipe#orVisitors --- .../main/java/org/openrewrite/config/DeclarativeRecipe.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java b/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java index 0ecfab865fd..513828bacaa 100644 --- a/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java +++ b/rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java @@ -280,6 +280,9 @@ public final List getRecipeList() { } private static TreeVisitor orVisitors(Recipe recipe) { + if (recipe.getRecipeList().isEmpty()) { + return recipe.getVisitor(); + } List> conditions = new ArrayList<>(); conditions.add(recipe.getVisitor()); for (Recipe r : recipe.getRecipeList()) { From 3dc8dde75ff64ea55eb9115fafece5725e5798e9 Mon Sep 17 00:00:00 2001 From: DidierLoiseau Date: Fri, 20 Sep 2024 18:04:17 +0200 Subject: [PATCH 73/82] Handle bom import in RemoveDuplicateDependencies (#4504) * Handle bom import in RemoveDuplicateDependencies * Minor polish --------- Co-authored-by: Tim te Beek --- .../maven/RemoveDuplicateDependencies.java | 14 ++ .../RemoveDuplicateDependenciesTest.java | 182 ++++++++++++------ 2 files changed, 133 insertions(+), 63 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveDuplicateDependencies.java b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveDuplicateDependencies.java index ae4ea359cb2..0d0c6cb7935 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveDuplicateDependencies.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveDuplicateDependencies.java @@ -125,6 +125,9 @@ private boolean isManagedDependenciesTag() { } private @Nullable DependencyKey getManagedDependencyKey(Xml.Tag tag) { + if (tag.getChildValue("scope").filter("import"::equalsIgnoreCase).isPresent()) { + return DependencyKey.from(tag); + } ResolvedManagedDependency resolvedDependency = findManagedDependency(tag); return resolvedDependency != null ? DependencyKey.from(resolvedDependency) : null; } @@ -151,5 +154,16 @@ public static DependencyKey from(ResolvedDependency dependency, Scope scope) { public static DependencyKey from(ResolvedManagedDependency dependency) { return new DependencyKey(dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), dependency.getClassifier(), Scope.Compile); } + + public static @Nullable DependencyKey from(Xml.Tag tag) { + return tag.getChildValue("artifactId").map(artifactId -> + new DependencyKey( + tag.getChildValue("groupId").orElse(null), + artifactId, + tag.getChildValue("type").orElse("jar"), + tag.getChildValue("classifier").orElse(null), + tag.getChildValue("scope").map(Scope::fromName).orElse(Scope.Compile) + )).orElse(null); + } } } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveDuplicateDependenciesTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveDuplicateDependenciesTest.java index aaa95f3e37b..ebacf75c684 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveDuplicateDependenciesTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveDuplicateDependenciesTest.java @@ -37,7 +37,7 @@ void notApplicable() { """ 4.0.0 - + com.mycompany.app my-app 1 @@ -55,11 +55,11 @@ void removeSingleDuplicate() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -83,11 +83,11 @@ void removeSingleDuplicate() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -114,11 +114,11 @@ void preservesComments() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -145,11 +145,11 @@ void preservesComments() { """ 4.0.0 - + com.mycompany.app my-app 1 - + @@ -179,11 +179,11 @@ void removeMultipleDuplicates() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -212,11 +212,11 @@ void removeMultipleDuplicates() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -243,11 +243,11 @@ void removeDependencyWithDifferentVersion() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.inject @@ -264,11 +264,11 @@ void removeDependencyWithDifferentVersion() { """, """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.inject @@ -289,11 +289,11 @@ void keepDependencyWithClassifier() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.inject @@ -313,7 +313,6 @@ void keepDependencyWithClassifier() { ); } - @Disabled("Unsure if this is a valid use case or not") @Test void keepDependencyWithType() { rewriteRun( @@ -321,11 +320,11 @@ void keepDependencyWithType() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -345,38 +344,38 @@ void keepDependencyWithType() { ); } - @Test - void keepDependencyManagementWithType() { - rewriteRun( - pomXml( - """ - - 4.0.0 - - com.mycompany.app - my-app - 1 - - - - - com.acme - example-dependency - 1.0.0 - - - com.acme - example-dependency - 1.0.0 - test-jar - - - - - """ - ) - ); - } + @Test + void keepDependencyManagementWithType() { + rewriteRun( + pomXml( + """ + + 4.0.0 + + com.mycompany.app + my-app + 1 + + + + + com.acme + example-dependency + 1.0.0 + + + com.acme + example-dependency + 1.0.0 + test-jar + + + + + """ + ) + ); + } @Test void keepDependencyWithDifferentScope() { @@ -385,11 +384,11 @@ void keepDependencyWithDifferentScope() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -410,6 +409,63 @@ void keepDependencyWithDifferentScope() { ); } + @Test + void removeDuplicatedDependencyWithImportScope() { + rewriteRun( + pomXml( + """ + + 4.0.0 + + com.mycompany.app + my-app + 1 + + + + + org.apache.logging.log4j + log4j-bom + 2.24.0 + import + pom + + + org.apache.logging.log4j + log4j-bom + 2.24.0 + import + pom + + + + + """, + """ + + 4.0.0 + + com.mycompany.app + my-app + 1 + + + + + org.apache.logging.log4j + log4j-bom + 2.24.0 + import + pom + + + + + """ + ) + ); + } + @Test void removeDependencyWithDefaultType() { rewriteRun( @@ -417,11 +473,11 @@ void removeDependencyWithDefaultType() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -440,11 +496,11 @@ void removeDependencyWithDefaultType() { """ 4.0.0 - + com.mycompany.app my-app 1 - + com.google.guava @@ -466,15 +522,15 @@ void retainDuplicateManagedDependenciesWithDifferentClassifier() { """ 4.0.0 - + com.mycompany.app my-app 1 - + 94.0.4606.61 - + From 20c44d090644b0e344e9b5859e3589f7b04d1521 Mon Sep 17 00:00:00 2001 From: DidierLoiseau Date: Fri, 20 Sep 2024 20:39:08 +0200 Subject: [PATCH 74/82] Handle plugins without groupId (#4507) * Handle plugins without groupId * Minor polish --------- Co-authored-by: Tim te Beek --- .../org/openrewrite/maven/MavenVisitor.java | 3 +- .../openrewrite/maven/internal/RawPom.java | 6 +- .../org/openrewrite/maven/tree/Plugin.java | 2 + ...RemoveRedundantDependencyVersionsTest.java | 165 ++++++++++++++++++ 4 files changed, 173 insertions(+), 3 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java index a6d658cd9ed..559586b65b1 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java @@ -31,6 +31,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static org.openrewrite.internal.StringUtils.matchesGlob; +import static org.openrewrite.maven.tree.Plugin.PLUGIN_DEFAULT_GROUPID; public class MavenVisitor

extends XmlVisitor

{ @@ -403,7 +404,7 @@ public boolean isDependencyLikeTag() { for (Plugin resolvedPlugin : plugins) { String reqGroup = resolvedPlugin.getGroupId(); String reqVersion = resolvedPlugin.getVersion(); - if ((reqGroup == null || reqGroup.equals(tag.getChildValue("groupId").orElse(null))) && + if (reqGroup.equals(tag.getChildValue("groupId").orElse(PLUGIN_DEFAULT_GROUPID)) && resolvedPlugin.getArtifactId().equals(tag.getChildValue("artifactId").orElse(null)) && (reqVersion == null || reqVersion.equals(tag.getChildValue("version").orElse(null)))) { return resolvedPlugin; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java index 2468380c964..ddd8a2c5730 100755 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/RawPom.java @@ -40,6 +40,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static org.openrewrite.maven.tree.Plugin.PLUGIN_DEFAULT_GROUPID; /** * A value object deserialized directly from POM XML @@ -249,6 +250,7 @@ public static class PluginManagement { @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Data public static class Plugin { + @Nullable String groupId; String artifactId; @@ -510,9 +512,9 @@ private List mapPlugins(@Nullable List(rawPlugins.size()); for (Plugin rawPlugin : rawPlugins) { - + String pluginGroupId = rawPlugin.getGroupId(); plugins.add(new org.openrewrite.maven.tree.Plugin( - rawPlugin.getGroupId(), + pluginGroupId == null ? PLUGIN_DEFAULT_GROUPID : pluginGroupId, rawPlugin.getArtifactId(), rawPlugin.getVersion(), rawPlugin.getExtensions(), diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Plugin.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Plugin.java index 0c9edde7db3..6da00f313cb 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Plugin.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/Plugin.java @@ -30,6 +30,8 @@ @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Value public class Plugin { + // default value as per https://maven.apache.org/xsd/maven-4.0.0.xsd + public static final String PLUGIN_DEFAULT_GROUPID = "org.apache.maven.plugins"; String groupId; String artifactId; diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java index 073d136b347..0ff8f2729ef 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/RemoveRedundantDependencyVersionsTest.java @@ -22,6 +22,7 @@ import org.openrewrite.Issue; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; import java.util.Collections; @@ -1290,6 +1291,170 @@ void removeRedundantVersionsFromPluginsManagedByParent() { ); } + @Test + void removeRedundantVersionsFromPluginsManagedByParentNotSpecifyingGroupId() { + rewriteRun( + spec -> spec.recipe(new RemoveRedundantDependencyVersions(null, null, RemoveRedundantDependencyVersions.Comparator.GTE, null)), + pomXml( + """ + + 4.0.0 + org.sample + parent + 1.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + + + + + maven-resources-plugin + + + + + + + """, SourceSpec::skip), + mavenProject("child", pomXml( + """ + + 4.0.0 + + org.sample + parent + 1.0.0 + + + sample + + + + maven-resources-plugin + 3.0.0 + + + + + + + + """, + """ + + 4.0.0 + + org.sample + parent + 1.0.0 + + + sample + + + + maven-resources-plugin + + + + + + + + """ + ) + ) + ); + } + + @Test + void removeRedundantDmVersionsFromPluginsManagedByParentNotSpecifyingGroupId() { + rewriteRun( + spec -> spec.recipe(new RemoveRedundantDependencyVersions(null, null, RemoveRedundantDependencyVersions.Comparator.GTE, null)), + pomXml( + """ + + 4.0.0 + org.sample + parent + 1.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + + + + + maven-resources-plugin + + + + + + + """, SourceSpec::skip), + mavenProject("child", pomXml( + """ + + 4.0.0 + + org.sample + parent + 1.0.0 + + + sample + + + + + maven-resources-plugin + 3.0.0 + + + + + + + + + """, + """ + + 4.0.0 + + org.sample + parent + 1.0.0 + + + sample + + + + + maven-resources-plugin + + + + + + + + + """ + ) + ) + ); + } + @Test void keepPluginIfParentInheritanceDisabled() { rewriteRun( From 93558127e5f5a4ae44ce409ea57da04eb6d4b3c2 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 21 Sep 2024 13:39:44 +0200 Subject: [PATCH 75/82] Drop `OperatorWrap` as there's a copy maintained in rewrite-static-analysis (#4509) * Add WrapOption to OperatorWrap to change detected style easily * Drop OperatorWrap, as there's a copy maintained in rewrite-static-analysis --- .../java/format/OperatorWrapTest.java | 627 ------------------ .../openrewrite/java/format/OperatorWrap.java | 423 ------------ 2 files changed, 1050 deletions(-) delete mode 100644 rewrite-java-test/src/test/java/org/openrewrite/java/format/OperatorWrapTest.java delete mode 100755 rewrite-java/src/main/java/org/openrewrite/java/format/OperatorWrap.java diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/format/OperatorWrapTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/format/OperatorWrapTest.java deleted file mode 100644 index b44ed214ee2..00000000000 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/format/OperatorWrapTest.java +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - *

- * 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 - *

- * https://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. - */ -package org.openrewrite.java.format; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.Tree; -import org.openrewrite.java.JavaParser; -import org.openrewrite.java.style.Checkstyle; -import org.openrewrite.java.style.OperatorWrapStyle; -import org.openrewrite.java.tree.J; -import org.openrewrite.style.NamedStyles; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; -import org.openrewrite.test.SourceSpec; - -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.UnaryOperator; - -import static java.util.Collections.emptySet; -import static java.util.Collections.singletonList; -import static org.openrewrite.java.Assertions.java; - -@SuppressWarnings({"StringConcatenationMissingWhitespace", "ConstantConditions", "CStyleArrayDeclaration"}) -class OperatorWrapTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new OperatorWrap()); - } - - private static List operatorWrapStyle() { - return operatorWrapStyle(style -> style); - } - - private static List operatorWrapStyle(UnaryOperator with) { - return Collections.singletonList( - new NamedStyles( - Tree.randomId(), "test", "test", "test", emptySet(), - singletonList(with.apply(Checkstyle.operatorWrapStyle())) - ) - ); - } - - @DocumentExample - @Test - void binaryOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), - java( - """ - class Test { - static void method() { - String s = "aaa" + - "b" + "c"; - } - } - """, - """ - class Test { - static void method() { - String s = "aaa" - + "b" + "c"; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void binaryOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), - java( - """ - class Test { - static void method() { - String s = "aaa" - + "b" + "c"; - } - } - """, - """ - class Test { - static void method() { - String s = "aaa" + - "b" + "c"; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void typeParameterOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), - java( - """ - import java.io.Serializable; - - class Test { - static > T method0() { - return null; - } - - static T method1() { - return null; - } - } - """, - """ - import java.io.Serializable; - - class Test { - static > T method0() { - return null; - } - - static T method1() { - return null; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void typeParameterOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), - java( - """ - import java.io.Serializable; - - class Test { - static > T method0() { - return null; - } - - static T method1() { - return null; - } - } - """, - """ - import java.io.Serializable; - - class Test { - static > T method0() { - return null; - } - - static T method1() { - return null; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void instanceOfOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), - java( - """ - class Test { - static Object method(Object s) { - if (s instanceof - String) { - return null; - } - return s; - } - } - """, - """ - class Test { - static Object method(Object s) { - if (s - instanceof String) { - return null; - } - return s; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void instanceOfOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), - java( - """ - class Test { - static Object method(Object s) { - if (s - instanceof String) { - return null; - } - return s; - } - } - """, - """ - class Test { - static Object method(Object s) { - if (s instanceof - String) { - return null; - } - return s; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void ternaryOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), - java( - """ - class Test { - static String method(String s) { - return s.contains("a") ? - "truePart" : - "falsePart"; - } - } - """, - """ - class Test { - static String method(String s) { - return s.contains("a") - ? "truePart" - : "falsePart"; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void ternaryOnNewlineIgnoringColon() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withColon(false)))), - java( - """ - class Test { - static String method(String s) { - return s.contains("a") ? - "truePart" : - "falsePart"; - } - } - """, - """ - class Test { - static String method(String s) { - return s.contains("a") - ? "truePart" : - "falsePart"; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void ternaryOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), - java( - """ - class Test { - static String method(String s) { - return s.contains("a") - ? "truePart" - : "falsePart"; - } - } - """, - """ - class Test { - static String method(String s) { - return s.contains("a") ? - "truePart" : - "falsePart"; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void assignmentOperatorOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withAssign(true) - .withDivAssign(true) - .withPlusAssign(true) - .withMinusAssign(true) - .withStarAssign(true) - .withModAssign(true) - .withSrAssign(true) - .withBsrAssign(true) - .withSlAssign(true) - .withBxorAssign(true) - .withBorAssign(true) - .withBandAssign(true) - ))), - java( - """ - class Test { - static int method() { - int a = 0; - a /= - 1; - a += - 1; - return a; - } - } - """, - """ - class Test { - static int method() { - int a = 0; - a - /= 1; - a - += 1; - return a; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void assignmentOperatorOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL) - .withAssign(true) - .withDivAssign(true) - .withPlusAssign(true) - .withMinusAssign(true) - .withStarAssign(true) - .withModAssign(true) - .withSrAssign(true) - .withBsrAssign(true) - .withSlAssign(true) - .withBxorAssign(true) - .withBorAssign(true) - .withBandAssign(true) - ))), - java( - """ - class Test { - static int method() { - int a = 0; - a - /= 1; - a - += 1; - return a; - } - } - """, - """ - class Test { - static int method() { - int a = 0; - a /= - 1; - a += - 1; - return a; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void memberReferenceOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withMethodRef(true)))), - java( - """ - import java.util.stream.Stream; - - class Test { - static void methodStream(Stream stream) { - stream.forEach(System.out:: - println); - } - } - """, - """ - import java.util.stream.Stream; - - class Test { - static void methodStream(Stream stream) { - stream.forEach(System.out - ::println); - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void memberReferenceOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL).withMethodRef(true)))), - java( - """ - import java.util.stream.Stream; - - class Test { - static void methodStream(Stream stream) { - stream.forEach(System.out - ::println); - } - } - """, - """ - import java.util.stream.Stream; - - class Test { - static void methodStream(Stream stream) { - stream.forEach(System.out:: - println); - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void assignmentOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withAssign(true)))), - java( - """ - class Test { - static int method() { - int n; - n = - 1; - return n; - } - } - """, - """ - class Test { - static int method() { - int n; - n - = 1; - return n; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void assignmentOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL).withAssign(true)))), - java( - """ - class Test { - static int method() { - int n; - n - = 1; - return n; - } - } - """, - """ - class Test { - static int method() { - int n; - n = - 1; - return n; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void variableOnNewline() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withAssign(true)))), - java( - """ - class Test { - static void method() { - int n = - 1; - int nArr[] = - new int[0]; - } - } - """, - """ - class Test { - static void method() { - int n - = 1; - int nArr[] - = new int[0]; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - @Test - void variableOnEndOfLine() { - rewriteRun( - spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> - style.withWrapOption(OperatorWrapStyle.WrapOption.EOL).withAssign(true)))), - java( - """ - class Test { - static void method() { - int n - = 1; - int nArr[] - = new int[0]; - } - } - """, - """ - class Test { - static void method() { - int n = - 1; - int nArr[] = - new int[0]; - } - } - """, - autoFormatIsIdempotent() - ) - ); - } - - private static Consumer> autoFormatIsIdempotent() { - return spec -> spec.afterRecipe(cu -> - Assertions.assertThat(new AutoFormatVisitor<>().visit(cu, 0)).isEqualTo(cu)); - } -} diff --git a/rewrite-java/src/main/java/org/openrewrite/java/format/OperatorWrap.java b/rewrite-java/src/main/java/org/openrewrite/java/format/OperatorWrap.java deleted file mode 100755 index 9412be2335a..00000000000 --- a/rewrite-java/src/main/java/org/openrewrite/java/format/OperatorWrap.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - *

- * 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 - *

- * https://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. - */ -package org.openrewrite.java.format; - -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; -import org.openrewrite.internal.ListUtils; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.style.Checkstyle; -import org.openrewrite.java.style.OperatorWrapStyle; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JRightPadded; -import org.openrewrite.java.tree.JavaSourceFile; -import org.openrewrite.java.tree.TypeTree; - -import static java.util.Objects.requireNonNull; - -public class OperatorWrap extends Recipe { - - @Override - public String getDisplayName() { - return "Operator wrapping"; - } - - @Override - public String getDescription() { - return "Fixes line wrapping policies on operators."; - } - - @Override - public TreeVisitor getVisitor() { - return new OperatorWrapVisitor(); - } - - private static class OperatorWrapVisitor extends JavaIsoVisitor { - OperatorWrapStyle operatorWrapStyle; - - @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof JavaSourceFile) { - SourceFile cu = (SourceFile) requireNonNull(tree); - operatorWrapStyle = cu.getStyle(OperatorWrapStyle.class) == null ? Checkstyle.operatorWrapStyle() : cu.getStyle(OperatorWrapStyle.class); - } - return super.visit(tree, ctx); - } - - @Override - public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { - J.Binary b = super.visitBinary(binary, ctx); - J.Binary.Type op = b.getOperator(); - if ((Boolean.TRUE.equals(operatorWrapStyle.getDiv()) && op == J.Binary.Type.Division) || - (Boolean.TRUE.equals(operatorWrapStyle.getStar()) && op == J.Binary.Type.Multiplication) || - (Boolean.TRUE.equals(operatorWrapStyle.getPlus()) && op == J.Binary.Type.Addition) || - (Boolean.TRUE.equals(operatorWrapStyle.getMinus()) && op == J.Binary.Type.Subtraction) || - (Boolean.TRUE.equals(operatorWrapStyle.getMod()) && op == J.Binary.Type.Modulo) || - (Boolean.TRUE.equals(operatorWrapStyle.getSr()) && op == J.Binary.Type.RightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getSl()) && op == J.Binary.Type.LeftShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getBsr()) && op == J.Binary.Type.UnsignedRightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getEqual()) && op == J.Binary.Type.Equal) || - (Boolean.TRUE.equals(operatorWrapStyle.getNotEqual()) && op == J.Binary.Type.NotEqual) || - (Boolean.TRUE.equals(operatorWrapStyle.getGt()) && op == J.Binary.Type.GreaterThan) || - (Boolean.TRUE.equals(operatorWrapStyle.getGe()) && op == J.Binary.Type.GreaterThanOrEqual) || - (Boolean.TRUE.equals(operatorWrapStyle.getLt()) && op == J.Binary.Type.LessThan) || - (Boolean.TRUE.equals(operatorWrapStyle.getLe()) && op == J.Binary.Type.LessThanOrEqual) || - (Boolean.TRUE.equals(operatorWrapStyle.getBand()) && op == J.Binary.Type.BitAnd) || - (Boolean.TRUE.equals(operatorWrapStyle.getBxor()) && op == J.Binary.Type.BitXor) || - (Boolean.TRUE.equals(operatorWrapStyle.getBor()) && op == J.Binary.Type.BitOr) || - (Boolean.TRUE.equals(operatorWrapStyle.getLand()) && op == J.Binary.Type.And) || - (Boolean.TRUE.equals(operatorWrapStyle.getLor()) && op == J.Binary.Type.Or)) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (b.getRight().getPrefix().getWhitespace().contains("\n")) { - b = b.getPadding().withOperator( - b.getPadding().getOperator().withBefore( - b.getRight().getPrefix() - ) - ); - b = b.withRight( - b.getRight().withPrefix( - b.getRight().getPrefix().withWhitespace(" ") - ) - ); - } - } else if (b.getPadding().getOperator().getBefore().getWhitespace().contains("\n")) { - b = b.withRight( - b.getRight().withPrefix( - b.getPadding().getOperator().getBefore() - ) - ); - b = b.getPadding().withOperator( - b.getPadding().getOperator().withBefore( - b.getRight().getPrefix().withWhitespace(" ") - ) - ); - } - } - return b; - } - - @Override - public J.TypeParameter visitTypeParameter(J.TypeParameter typeParam, ExecutionContext ctx) { - J.TypeParameter tp = super.visitTypeParameter(typeParam, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getTypeExtensionAnd()) && tp.getPadding().getBounds() != null) { - int typeBoundsSize = tp.getPadding().getBounds().getPadding().getElements().size(); - tp = tp.getPadding().withBounds( - tp.getPadding().getBounds().getPadding().withElements( - ListUtils.map(tp.getPadding().getBounds().getPadding().getElements(), - (index, elemContainer) -> { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (index != typeBoundsSize - 1 && typeParam.getPadding().getBounds() != null) { - JRightPadded next = typeParam.getPadding().getBounds().getPadding().getElements().get(index + 1); - if (next.getElement().getPrefix().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withAfter( - next.getElement().getPrefix() - ); - } - } else { - if (elemContainer.getElement().getPrefix().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withElement( - elemContainer.getElement().withPrefix( - elemContainer.getElement().getPrefix().withWhitespace(" ") - ) - ); - } - } - } else { - if (index != typeBoundsSize - 1) { - if (elemContainer.getAfter().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withAfter( - elemContainer.getAfter().withWhitespace(" ") - ); - } - } else if (typeBoundsSize > 1 && typeParam.getPadding().getBounds() != null) { - JRightPadded previous = typeParam.getPadding().getBounds().getPadding().getElements().get(index - 1); - if (previous.getAfter().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withElement( - elemContainer.getElement().withPrefix( - previous.getAfter() - ) - ); - } - } - } - return elemContainer; - } - ) - ) - ); - } - return tp; - } - - @Override - public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, ExecutionContext ctx) { - J.InstanceOf i = super.visitInstanceOf(instanceOf, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getLiteralInstanceof())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (i.getClazz().getPrefix().getWhitespace().contains("\n")) { - i = i.getPadding().withExpression( - i.getPadding().getExpression().withAfter( - i.getClazz().getPrefix() - ) - ); - i = i.withClazz( - i.getClazz().withPrefix( - i.getClazz().getPrefix().withWhitespace(" ") - ) - ); - } - } else if (i.getPadding().getExpression().getAfter().getWhitespace().contains("\n")) { - i = i.withClazz( - i.getClazz().withPrefix( - i.getPadding().getExpression().getAfter() - ) - ); - i = i.getPadding().withExpression( - i.getPadding().getExpression().withAfter( - i.getPadding().getExpression().getAfter().withWhitespace(" ") - ) - ); - } - } - return i; - } - - @Override - public J.Ternary visitTernary(J.Ternary ternary, ExecutionContext ctx) { - J.Ternary t = super.visitTernary(ternary, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getQuestion())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (t.getTruePart().getPrefix().getWhitespace().contains("\n")) { - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withBefore( - t.getPadding().getTruePart().getElement().getPrefix() - ) - ); - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withElement( - t.getPadding().getTruePart().getElement().withPrefix( - t.getPadding().getTruePart().getElement().getPrefix().withWhitespace(" ") - ) - ) - ); - } - } else if (t.getPadding().getTruePart().getBefore().getWhitespace().contains("\n")) { - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withElement( - t.getPadding().getTruePart().getElement().withPrefix( - t.getPadding().getTruePart().getBefore() - ) - ) - ); - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withBefore( - t.getPadding().getTruePart().getElement().getPrefix().withWhitespace(" ") - ) - ); - } - } - if (Boolean.TRUE.equals(operatorWrapStyle.getColon())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (t.getPadding().getFalsePart().getElement().getPrefix().getWhitespace().contains("\n")) { - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withBefore( - t.getPadding().getFalsePart().getElement().getPrefix() - ) - ); - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withElement( - t.getPadding().getFalsePart().getElement().withPrefix( - t.getPadding().getFalsePart().getElement().getPrefix().withWhitespace(" ") - ) - ) - ); - } - } else if (t.getPadding().getFalsePart().getBefore().getWhitespace().contains("\n")) { - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withElement( - t.getPadding().getFalsePart().getElement().withPrefix( - t.getPadding().getFalsePart().getBefore() - ) - ) - ); - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withBefore( - t.getPadding().getFalsePart().getElement().getPrefix().withWhitespace(" ") - ) - ); - } - } - return t; - } - - @Override - public J.MemberReference visitMemberReference(J.MemberReference memberRef, ExecutionContext ctx) { - J.MemberReference m = super.visitMemberReference(memberRef, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getMethodRef())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (m.getPadding().getReference().getBefore().getWhitespace().contains("\n")) { - m = m.getPadding().withContaining( - m.getPadding().getContaining().withAfter( - m.getPadding().getReference().getBefore() - ) - ); - m = m.getPadding().withReference( - m.getPadding().getReference().withBefore( - m.getPadding().getReference().getBefore().withWhitespace("") - ) - ); - } - } else if (m.getPadding().getContaining().getAfter().getWhitespace().contains("\n")) { - m = m.getPadding().withReference( - m.getPadding().getReference().withBefore( - m.getPadding().getContaining().getAfter() - ) - ); - m = m.getPadding().withContaining( - m.getPadding().getContaining().withAfter( - m.getPadding().getReference().getBefore().withWhitespace("") - ) - ); - } - } - return m; - } - - @Override - public J.Assignment visitAssignment(J.Assignment assignment, ExecutionContext ctx) { - J.Assignment a = super.visitAssignment(assignment, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getAssign())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (a.getPadding().getAssignment().getElement().getPrefix().getWhitespace().contains("\n")) { - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withBefore( - a.getPadding().getAssignment().getElement().getPrefix() - ) - ); - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withElement( - a.getPadding().getAssignment().getElement().withPrefix( - a.getPadding().getAssignment().getElement().getPrefix().withWhitespace(" ") - ) - ) - ); - } - } else if (a.getPadding().getAssignment().getBefore().getWhitespace().contains("\n")) { - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withElement( - a.getPadding().getAssignment().getElement().withPrefix( - a.getPadding().getAssignment().getBefore() - ) - ) - ); - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withBefore( - a.getPadding().getAssignment().getBefore().withWhitespace(" ") - ) - ); - } - } - return a; - } - - @Override - public J.AssignmentOperation visitAssignmentOperation(J.AssignmentOperation assignOp, ExecutionContext ctx) { - J.AssignmentOperation a = super.visitAssignmentOperation(assignOp, ctx); - J.AssignmentOperation.Type op = a.getOperator(); - if ((Boolean.TRUE.equals(operatorWrapStyle.getPlusAssign()) && op == J.AssignmentOperation.Type.Addition) || - (Boolean.TRUE.equals(operatorWrapStyle.getMinusAssign()) && op == J.AssignmentOperation.Type.Subtraction) || - (Boolean.TRUE.equals(operatorWrapStyle.getStarAssign()) && op == J.AssignmentOperation.Type.Multiplication) || - (Boolean.TRUE.equals(operatorWrapStyle.getDivAssign()) && op == J.AssignmentOperation.Type.Division) || - (Boolean.TRUE.equals(operatorWrapStyle.getModAssign()) && op == J.AssignmentOperation.Type.Modulo) || - (Boolean.TRUE.equals(operatorWrapStyle.getSrAssign()) && op == J.AssignmentOperation.Type.RightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getSlAssign()) && op == J.AssignmentOperation.Type.LeftShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getBsrAssign()) && op == J.AssignmentOperation.Type.UnsignedRightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getBandAssign()) && op == J.AssignmentOperation.Type.BitAnd) || - (Boolean.TRUE.equals(operatorWrapStyle.getBxorAssign()) && op == J.AssignmentOperation.Type.BitXor) || - (Boolean.TRUE.equals(operatorWrapStyle.getBorAssign()) && op == J.AssignmentOperation.Type.BitOr)) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (a.getAssignment().getPrefix().getWhitespace().contains("\n")) { - a = a.getPadding().withOperator( - a.getPadding().getOperator().withBefore( - a.getAssignment().getPrefix() - ) - ); - a = a.withAssignment( - a.getAssignment().withPrefix( - a.getAssignment().getPrefix().withWhitespace(" ") - ) - ); - } - } else if (a.getPadding().getOperator().getBefore().getWhitespace().contains("\n")) { - a = a.withAssignment( - a.getAssignment().withPrefix( - a.getPadding().getOperator().getBefore() - ) - ); - a = a.getPadding().withOperator( - a.getPadding().getOperator().withBefore( - a.getAssignment().getPrefix().withWhitespace(" ") - ) - ); - } - } - return a; - } - - @Override - public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { - J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getAssign()) && v.getPadding().getInitializer() != null) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (v.getPadding().getInitializer().getElement().getPrefix().getWhitespace().contains("\n")) { - v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withBefore( - v.getPadding().getInitializer().getElement().getPrefix() - ) - ); - if (v.getPadding().getInitializer() != null && v.getPadding().getInitializer().getElement() != null) { - v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withElement( - v.getPadding().getInitializer().getElement().withPrefix( - v.getPadding().getInitializer().getElement().getPrefix().withWhitespace(" ") - ) - ) - ); - } - } - } else if (v.getPadding().getInitializer().getBefore().getWhitespace().contains("\n")) { - v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withElement( - v.getPadding().getInitializer().getElement().withPrefix( - v.getPadding().getInitializer().getBefore() - ) - ) - ); - if (v.getPadding().getInitializer() != null && v.getPadding().getInitializer().getBefore() != null) { - v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withBefore( - v.getPadding().getInitializer().getElement().getPrefix().withWhitespace(" ") - ) - ); - } - } - } - return v; - } - - } - -} From 25d374b7c9500c6651dff001f62ea15cff4f9702 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 21 Sep 2024 17:35:54 +0200 Subject: [PATCH 76/82] refactor: Operator wrapping on end of line (#4510) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.staticanalysis.OperatorWrap?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sidmFsdWUiOiJFT0wiLCJuYW1lIjoid3JhcE9wdGlvbiJ9XQ== Co-authored-by: Moderne --- .../src/main/java/org/openrewrite/Cursor.java | 4 +-- .../main/java/org/openrewrite/PathUtils.java | 10 +++--- .../org/openrewrite/internal/StringUtils.java | 10 +++--- .../openrewrite/ipc/http/OkHttpSender.java | 6 ++-- .../org/openrewrite/marker/GitProvenance.java | 6 ++-- .../marker/ci/BitbucketBuildEnvironment.java | 6 ++-- .../marker/ci/BuildEnvironment.java | 4 +-- .../marker/ci/CustomBuildEnvironment.java | 6 ++-- .../marker/ci/DroneBuildEnvironment.java | 6 ++-- .../ci/GithubActionsBuildEnvironment.java | 12 +++---- .../marker/ci/GitlabBuildEnvironment.java | 6 ++-- .../openrewrite/semver/DependencyMatcher.java | 6 ++-- .../openrewrite/text/AppendToTextFile.java | 12 +++---- .../gradle/ChangeDependencyArtifactId.java | 10 +++--- .../gradle/ChangeDependencyClassifier.java | 8 ++--- .../gradle/ChangeDependencyExtension.java | 8 ++--- .../gradle/ChangeDependencyGroupId.java | 10 +++--- .../gradle/ChangeExtraProperty.java | 4 +-- .../gradle/DependencyConstraintToRule.java | 10 +++--- .../gradle/DependencyUseMapNotation.java | 4 +-- .../openrewrite/gradle/RemoveDependency.java | 4 +-- .../gradle/UpdateJavaCompatibility.java | 8 ++--- .../gradle/UpgradeDependencyVersion.java | 12 +++---- .../gradle/plugins/AddPluginVisitor.java | 8 ++--- .../plugins/AddSettingsPluginRepository.java | 8 ++--- .../gradle/plugins/RemovePluginVisitor.java | 32 +++++++++---------- .../gradle/search/FindRepository.java | 32 +++++++++---------- .../gradle/util/ChangeStringLiteral.java | 4 +-- .../groovy/GroovyParserVisitor.java | 32 +++++++++---------- .../hcl/search/FindAndReplaceLiteral.java | 4 +-- ...ReloadableJava11ParserInputFileObject.java | 4 +-- .../java/isolated/ReloadableJava17Parser.java | 4 +-- ...ReloadableJava17ParserInputFileObject.java | 4 +-- .../java/isolated/ReloadableJava21Parser.java | 4 +-- ...ReloadableJava21ParserInputFileObject.java | 4 +-- .../java/Java8ParserInputFileObject.java | 4 +-- .../java/JavaTemplateMatchTest.java | 6 ++-- .../java/JavaTemplateTest4Test.java | 8 ++--- .../ArchiveAnalyzer.java | 24 +++++++------- .../dataflow-functional-tests/FileUtils.java | 16 +++++----- .../GitCommandLineUtils.java | 4 +-- .../java/org/openrewrite/java/Assertions.java | 4 +-- .../org/openrewrite/java/ChangePackage.java | 6 ++-- .../java/ChangeStaticFieldToMethod.java | 4 +-- .../openrewrite/java/ImplementInterface.java | 4 +-- .../org/openrewrite/java/MethodMatcher.java | 20 ++++++------ .../openrewrite/java/QualifyThisVisitor.java | 6 ++-- .../org/openrewrite/java/RemoveImport.java | 12 +++---- .../ShortenFullyQualifiedTypeReferences.java | 6 ++-- .../openrewrite/java/TreeVisitingPrinter.java | 6 ++-- .../SimplifyBooleanExpressionVisitor.java | 6 ++-- .../BlockStatementTemplateGenerator.java | 6 ++-- .../internal/template/PatternVariables.java | 22 ++++++------- .../java/internal/template/Substitutions.java | 4 +-- .../SourceSpecTextBlockIndentation.java | 4 +-- .../java/search/FindEmptyMethods.java | 6 ++-- .../openrewrite/java/search/FindMethods.java | 4 +-- .../java/search/FindMissingTypes.java | 20 ++++++------ .../java/search/SemanticallyEqual.java | 4 +-- .../org/openrewrite/java/search/UsesType.java | 4 +-- .../openrewrite/java/style/Autodetect.java | 4 +-- .../java/style/CheckstyleConfigLoader.java | 4 +-- .../java/org/openrewrite/java/tree/J.java | 12 +++---- .../org/openrewrite/java/tree/JavaType.java | 4 +-- .../org/openrewrite/java/tree/TypeUtils.java | 4 +-- .../org/openrewrite/maven/AddDependency.java | 8 ++--- .../maven/AddManagedDependency.java | 6 ++-- .../maven/AddPluginDependency.java | 8 ++--- .../org/openrewrite/maven/AddProperty.java | 4 +-- .../openrewrite/maven/AddPropertyVisitor.java | 6 ++-- .../org/openrewrite/maven/AddRepository.java | 12 +++---- .../openrewrite/maven/ChangeParentPom.java | 8 ++--- .../maven/IncrementProjectVersion.java | 4 +-- .../org/openrewrite/maven/MavenVisitor.java | 4 +-- .../maven/RemovePluginDependency.java | 4 +-- .../RemoveRedundantDependencyVersions.java | 4 +-- .../openrewrite/maven/UpdateMavenWrapper.java | 4 +-- .../maven/UpgradeDependencyVersion.java | 6 ++-- .../maven/UpgradePluginVersion.java | 6 ++-- .../maven/internal/MavenPomDownloader.java | 8 ++--- .../search/DoesNotIncludeDependency.java | 4 +-- .../openrewrite/maven/tree/ResolvedPom.java | 10 +++--- .../properties/ChangePropertyKey.java | 12 +++---- .../properties/ChangePropertyValue.java | 18 +++++------ .../org/openrewrite/test/RewriteTest.java | 12 +++---- .../openrewrite/xml/ChangeNamespaceValue.java | 10 +++--- .../openrewrite/xml/ChangeTagAttribute.java | 6 ++-- .../org/openrewrite/xml/XPathMatcher.java | 14 ++++---- .../org/openrewrite/xml/trait/Namespaced.java | 4 +-- .../openrewrite/yaml/ChangePropertyKey.java | 16 +++++----- .../openrewrite/yaml/ChangePropertyValue.java | 18 +++++------ .../org/openrewrite/yaml/DeleteProperty.java | 4 +-- 92 files changed, 385 insertions(+), 385 deletions(-) diff --git a/rewrite-core/src/main/java/org/openrewrite/Cursor.java b/rewrite-core/src/main/java/org/openrewrite/Cursor.java index 969872bc044..3be9d7c15a3 100644 --- a/rewrite-core/src/main/java/org/openrewrite/Cursor.java +++ b/rewrite-core/src/main/java/org/openrewrite/Cursor.java @@ -187,8 +187,8 @@ public String toString() { .map(t -> t instanceof Tree ? t.getClass().getSimpleName() : t.toString()) - .collect(Collectors.joining("->")) - + "}"; + .collect(Collectors.joining("->")) + + "}"; } public Cursor dropParentUntil(Predicate valuePredicate) { diff --git a/rewrite-core/src/main/java/org/openrewrite/PathUtils.java b/rewrite-core/src/main/java/org/openrewrite/PathUtils.java index 14e7c65e06b..96213005bf7 100755 --- a/rewrite-core/src/main/java/org/openrewrite/PathUtils.java +++ b/rewrite-core/src/main/java/org/openrewrite/PathUtils.java @@ -151,8 +151,8 @@ private static boolean matchesGlob(String pattern, String path) { if (!StringUtils.matchesGlob(pathTokens[pathIdxEnd], pattTokens[pattIdxEnd])) { return false; } - if (pattIdxEnd == (pattTokens.length - 1) - && (isFileSeparator(pattern.charAt(pattern.length() - 1)) ^ isFileSeparator(path.charAt(path.length() - 1)))) { + if (pattIdxEnd == (pattTokens.length - 1) && + (isFileSeparator(pattern.charAt(pattern.length() - 1)) ^ isFileSeparator(path.charAt(path.length() - 1)))) { return false; } pattIdxEnd--; @@ -293,8 +293,8 @@ private static boolean isFileSeparator(char ch) { @SuppressWarnings("SameParameterValue") private static boolean isFileSeparator(boolean strict, char ch) { - return strict - ? ch == File.separatorChar - : ch == UNIX_SEPARATOR || ch == WINDOWS_SEPARATOR; + return strict ? + ch == File.separatorChar : + ch == UNIX_SEPARATOR || ch == WINDOWS_SEPARATOR; } } diff --git a/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java b/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java index bdeaaa07585..80e068c38f2 100644 --- a/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java +++ b/rewrite-core/src/main/java/org/openrewrite/internal/StringUtils.java @@ -466,8 +466,8 @@ private static boolean matchesGlob(String pattern, String str, boolean caseSensi if (ch == '*') { break; } - if (ch != '?' - && different(caseSensitive, ch, str.charAt(strIdxStart))) { + if (ch != '?' && + different(caseSensitive, ch, str.charAt(strIdxStart))) { return false; // Character mismatch } patIdxStart++; @@ -554,9 +554,9 @@ private static boolean allStars(String chars, int start, int end) { } private static boolean different(boolean caseSensitive, char ch, char other) { - return caseSensitive - ? ch != other - : Character.toUpperCase(ch) != Character.toUpperCase(other); + return caseSensitive ? + ch != other : + Character.toUpperCase(ch) != Character.toUpperCase(other); } public static String indent(String text) { diff --git a/rewrite-core/src/main/java/org/openrewrite/ipc/http/OkHttpSender.java b/rewrite-core/src/main/java/org/openrewrite/ipc/http/OkHttpSender.java index acaba870a50..eb42f70c913 100644 --- a/rewrite-core/src/main/java/org/openrewrite/ipc/http/OkHttpSender.java +++ b/rewrite-core/src/main/java/org/openrewrite/ipc/http/OkHttpSender.java @@ -54,9 +54,9 @@ public Response send(Request request) { String methodValue = method.toString(); if (entity.length > 0) { String contentType = request.getRequestHeaders().get("Content-Type"); - MediaType mediaType = contentType != null - ? MediaType.get(contentType + "; charset=utf-8") - : MEDIA_TYPE_APPLICATION_JSON; + MediaType mediaType = contentType != null ? + MediaType.get(contentType + "; charset=utf-8") : + MEDIA_TYPE_APPLICATION_JSON; RequestBody body = RequestBody.create(entity, mediaType); requestBuilder.method(methodValue, body); } else { diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/GitProvenance.java b/rewrite-core/src/main/java/org/openrewrite/marker/GitProvenance.java index a6775ddab74..b2bb51c7077 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/GitProvenance.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/GitProvenance.java @@ -195,9 +195,9 @@ public GitProvenance(UUID id, if (environment instanceof JenkinsBuildEnvironment) { JenkinsBuildEnvironment jenkinsBuildEnvironment = (JenkinsBuildEnvironment) environment; try (Repository repository = new RepositoryBuilder().findGitDir(projectDir.toFile()).build()) { - String branch = jenkinsBuildEnvironment.getLocalBranch() != null - ? jenkinsBuildEnvironment.getLocalBranch() - : localBranchName(repository, jenkinsBuildEnvironment.getBranch()); + String branch = jenkinsBuildEnvironment.getLocalBranch() != null ? + jenkinsBuildEnvironment.getLocalBranch() : + localBranchName(repository, jenkinsBuildEnvironment.getBranch()); return fromGitConfig(repository, branch, getChangeset(repository), gitRemoteParser); } catch (IllegalArgumentException | GitAPIException e) { // Silently ignore if the project directory is not a git repository diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/ci/BitbucketBuildEnvironment.java b/rewrite-core/src/main/java/org/openrewrite/marker/ci/BitbucketBuildEnvironment.java index ad2d33667f6..b0220e01f76 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/ci/BitbucketBuildEnvironment.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/ci/BitbucketBuildEnvironment.java @@ -47,9 +47,9 @@ public static BitbucketBuildEnvironment build(UnaryOperator environment) @Override public GitProvenance buildGitProvenance() throws IncompleteGitConfigException { - if (StringUtils.isBlank(httpOrigin) - || StringUtils.isBlank(branch) - || StringUtils.isBlank(sha)) { + if (StringUtils.isBlank(httpOrigin) || + StringUtils.isBlank(branch) || + StringUtils.isBlank(sha)) { throw new IncompleteGitConfigException(); } else { return new GitProvenance(UUID.randomUUID(), httpOrigin, branch, sha, diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/ci/BuildEnvironment.java b/rewrite-core/src/main/java/org/openrewrite/marker/ci/BuildEnvironment.java index 0cb78df5f93..47cac47db41 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/ci/BuildEnvironment.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/ci/BuildEnvironment.java @@ -36,8 +36,8 @@ public interface BuildEnvironment extends Marker { if (environment.apply("GITLAB_CI") != null) { return GitlabBuildEnvironment.build(environment); } - if (environment.apply("CI") != null && environment.apply("GITHUB_ACTION") != null - && environment.apply("GITHUB_RUN_ID") != null) { + if (environment.apply("CI") != null && environment.apply("GITHUB_ACTION") != null && + environment.apply("GITHUB_RUN_ID") != null) { return GithubActionsBuildEnvironment.build(environment); } if (environment.apply("DRONE") != null) { diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/ci/CustomBuildEnvironment.java b/rewrite-core/src/main/java/org/openrewrite/marker/ci/CustomBuildEnvironment.java index 75235b9c6fc..ee1f22c1ac9 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/ci/CustomBuildEnvironment.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/ci/CustomBuildEnvironment.java @@ -47,9 +47,9 @@ public static CustomBuildEnvironment build(UnaryOperator environment) { @Override public GitProvenance buildGitProvenance() throws IncompleteGitConfigException { - if (StringUtils.isBlank(cloneURL) - || StringUtils.isBlank(ref) - || StringUtils.isBlank(sha)) { + if (StringUtils.isBlank(cloneURL) || + StringUtils.isBlank(ref) || + StringUtils.isBlank(sha)) { throw new IncompleteGitConfigException(); } else { return new GitProvenance(UUID.randomUUID(), cloneURL, ref, sha, diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/ci/DroneBuildEnvironment.java b/rewrite-core/src/main/java/org/openrewrite/marker/ci/DroneBuildEnvironment.java index f5abc81ac71..b1d61512e59 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/ci/DroneBuildEnvironment.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/ci/DroneBuildEnvironment.java @@ -62,9 +62,9 @@ public static DroneBuildEnvironment build(UnaryOperator environment) { @Override public GitProvenance buildGitProvenance() throws IncompleteGitConfigException { - if (StringUtils.isBlank(remoteURL) - || (StringUtils.isBlank(branch) && StringUtils.isBlank(tag)) - || StringUtils.isBlank(commitSha)) { + if (StringUtils.isBlank(remoteURL) || + (StringUtils.isBlank(branch) && StringUtils.isBlank(tag)) || + StringUtils.isBlank(commitSha)) { throw new IncompleteGitConfigException(); } return new GitProvenance(UUID.randomUUID(), remoteURL, diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/ci/GithubActionsBuildEnvironment.java b/rewrite-core/src/main/java/org/openrewrite/marker/ci/GithubActionsBuildEnvironment.java index c1359b523b2..cb5ca645c6a 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/ci/GithubActionsBuildEnvironment.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/ci/GithubActionsBuildEnvironment.java @@ -67,16 +67,16 @@ public GitProvenance buildGitProvenance() throws IncompleteGitConfigException { } else { gitRef = gitRef.replaceFirst("refs/heads/", ""); } - if (StringUtils.isBlank(ghRef) - || StringUtils.isBlank(host) - || StringUtils.isBlank(repository) - || StringUtils.isBlank(sha)) { + if (StringUtils.isBlank(ghRef) || + StringUtils.isBlank(host) || + StringUtils.isBlank(repository) || + StringUtils.isBlank(sha)) { throw new IncompleteGitConfigException( String.format("Invalid GitHub environment with host: %s, branch: %s, " + "repository: %s, sha: %s", host, ghRef, repository, sha)); } - return new GitProvenance(UUID.randomUUID(), host + "/" + getRepository() - + ".git", gitRef, getSha(), null, null, emptyList()); + return new GitProvenance(UUID.randomUUID(), host + "/" + getRepository() + + ".git", gitRef, getSha(), null, null, emptyList()); } } diff --git a/rewrite-core/src/main/java/org/openrewrite/marker/ci/GitlabBuildEnvironment.java b/rewrite-core/src/main/java/org/openrewrite/marker/ci/GitlabBuildEnvironment.java index 276c52c96ef..b1fecbbd412 100644 --- a/rewrite-core/src/main/java/org/openrewrite/marker/ci/GitlabBuildEnvironment.java +++ b/rewrite-core/src/main/java/org/openrewrite/marker/ci/GitlabBuildEnvironment.java @@ -56,9 +56,9 @@ public static GitlabBuildEnvironment build(UnaryOperator environment) { @Override public GitProvenance buildGitProvenance() throws IncompleteGitConfigException { - if (StringUtils.isBlank(ciRepositoryUrl) - || StringUtils.isBlank(ciCommitRefName) - || StringUtils.isBlank(ciCommitSha)) { + if (StringUtils.isBlank(ciRepositoryUrl) || + StringUtils.isBlank(ciCommitRefName) || + StringUtils.isBlank(ciCommitSha)) { throw new IncompleteGitConfigException(); } return new GitProvenance(UUID.randomUUID(), ciRepositoryUrl, ciCommitRefName, ciCommitSha, diff --git a/rewrite-core/src/main/java/org/openrewrite/semver/DependencyMatcher.java b/rewrite-core/src/main/java/org/openrewrite/semver/DependencyMatcher.java index fe6c7032705..0d75d0655db 100755 --- a/rewrite-core/src/main/java/org/openrewrite/semver/DependencyMatcher.java +++ b/rewrite-core/src/main/java/org/openrewrite/semver/DependencyMatcher.java @@ -75,9 +75,9 @@ public static Validated build(String pattern) { } public boolean matches(@Nullable String groupId, String artifactId, String version) { - return StringUtils.matchesGlob(groupId, groupPattern) - && StringUtils.matchesGlob(artifactId, artifactPattern) - && (versionComparator == null || versionComparator.isValid(null, version)); + return StringUtils.matchesGlob(groupId, groupPattern) && + StringUtils.matchesGlob(artifactId, artifactPattern) && + (versionComparator == null || versionComparator.isValid(null, version)); } public boolean matches(@Nullable String groupId, String artifactId) { diff --git a/rewrite-core/src/main/java/org/openrewrite/text/AppendToTextFile.java b/rewrite-core/src/main/java/org/openrewrite/text/AppendToTextFile.java index bb435c61c80..1f130cafa7e 100644 --- a/rewrite-core/src/main/java/org/openrewrite/text/AppendToTextFile.java +++ b/rewrite-core/src/main/java/org/openrewrite/text/AppendToTextFile.java @@ -53,12 +53,12 @@ public class AppendToTextFile extends ScanningRecipe { @Nullable Boolean appendNewline; @Option(displayName = "Existing file strategy", - description = "Determines behavior if a file exists at this location prior to Rewrite execution.\n\n" - + "- `Continue`: append new content to existing file contents. If existing file is not plaintext, recipe does nothing.\n" - + "- `Replace`: remove existing content from file.\n" - + "- `Leave`: *(default)* do nothing. Existing file is fully preserved.\n\n" - + "Note: this only affects the first interaction with the specified file per Rewrite execution.\n" - + "Subsequent instances of this recipe in the same Rewrite execution will always append.", + description = "Determines behavior if a file exists at this location prior to Rewrite execution.\n\n" + + "- `Continue`: append new content to existing file contents. If existing file is not plaintext, recipe does nothing.\n" + + "- `Replace`: remove existing content from file.\n" + + "- `Leave`: *(default)* do nothing. Existing file is fully preserved.\n\n" + + "Note: this only affects the first interaction with the specified file per Rewrite execution.\n" + + "Subsequent instances of this recipe in the same Rewrite execution will always append.", valid = {"Continue", "Replace", "Leave"}, required = false) @Nullable Strategy existingFileStrategy; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java index 8220bbae2a6..d54a40c6b77 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyArtifactId.java @@ -151,8 +151,8 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m) { if (strings.size() >= 2 && strings.get(0) instanceof J.Literal) { Dependency dependency = DependencyStringNotationConverter.parse((String) requireNonNull(((J.Literal) strings.get(0)).getValue())); - if (dependency != null && !newArtifactId.equals(dependency.getArtifactId()) - && depMatcher.matches(dependency.getGroupId(), dependency.getArtifactId())) { + if (dependency != null && !newArtifactId.equals(dependency.getArtifactId()) && + depMatcher.matches(dependency.getGroupId(), dependency.getArtifactId())) { Dependency newDependency = dependency.withArtifactId(newArtifactId); updatedDependencies.put(dependency.getGav().asGroupArtifact(), newDependency.getGav().asGroupArtifact()); String replacement = newDependency.toStringNotation(); @@ -196,9 +196,9 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m) { version = valueValue; } } - if (groupId == null || artifactId == null - || (version == null && !depMatcher.matches(groupId, artifactId)) - || (version != null && !depMatcher.matches(groupId, artifactId, version))) { + if (groupId == null || artifactId == null || + (version == null && !depMatcher.matches(groupId, artifactId)) || + (version != null && !depMatcher.matches(groupId, artifactId, version))) { return m; } String delimiter = versionStringDelimiter; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java index c2698f806b9..c11f7ccd68d 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyClassifier.java @@ -157,10 +157,10 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) } index++; } - if (groupId == null || artifactId == null - || (version == null && !depMatcher.matches(groupId, artifactId)) - || (version != null && !depMatcher.matches(groupId, artifactId, version)) - || Objects.equals(newClassifier, classifier)) { + if (groupId == null || artifactId == null || + (version == null && !depMatcher.matches(groupId, artifactId)) || + (version != null && !depMatcher.matches(groupId, artifactId, version)) || + Objects.equals(newClassifier, classifier)) { return m; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java index 9847fd08be7..a6a617f627b 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyExtension.java @@ -146,10 +146,10 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) extension = valueValue; } } - if (groupId == null || artifactId == null - || (version == null && !depMatcher.matches(groupId, artifactId)) - || (version != null && !depMatcher.matches(groupId, artifactId, version)) - || extension == null) { + if (groupId == null || artifactId == null || + (version == null && !depMatcher.matches(groupId, artifactId)) || + (version != null && !depMatcher.matches(groupId, artifactId, version)) || + extension == null) { return m; } String delimiter = extensionStringDelimiter; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java index 6f361ce6b94..550b71f1492 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeDependencyGroupId.java @@ -151,8 +151,8 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m) { if (strings.size() >= 2 && strings.get(0) instanceof J.Literal) { Dependency dependency = DependencyStringNotationConverter.parse((String) requireNonNull(((J.Literal) strings.get(0)).getValue())); - if (dependency != null && !newGroupId.equals(dependency.getGroupId()) - && depMatcher.matches(dependency.getGroupId(), dependency.getArtifactId())) { + if (dependency != null && !newGroupId.equals(dependency.getGroupId()) && + depMatcher.matches(dependency.getGroupId(), dependency.getArtifactId())) { Dependency newDependency = dependency.withGroupId(newGroupId); updatedDependencies.put(dependency.getGav().asGroupArtifact(), newDependency.getGav().asGroupArtifact()); String replacement = newDependency.toStringNotation(); @@ -196,9 +196,9 @@ private J.MethodInvocation updateDependency(J.MethodInvocation m) { version = valueValue; } } - if (groupId == null || artifactId == null - || (version == null && !depMatcher.matches(groupId, artifactId)) - || (version != null && !depMatcher.matches(groupId, artifactId, version))) { + if (groupId == null || artifactId == null || + (version == null && !depMatcher.matches(groupId, artifactId)) || + (version != null && !depMatcher.matches(groupId, artifactId, version))) { return m; } String delimiter = versionStringDelimiter; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeExtraProperty.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeExtraProperty.java index 060980259ee..ca1726e8b7b 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeExtraProperty.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/ChangeExtraProperty.java @@ -75,8 +75,8 @@ public J.Assignment visitAssignment(J.Assignment as, ExecutionContext ctx) { if(!Objects.equals(key, var.getSimpleName())) { return as; } - if((var.getTarget() instanceof J.Identifier && ((J.Identifier) var.getTarget()).getSimpleName().equals("ext")) - || (var.getTarget() instanceof J.FieldAccess && ((J.FieldAccess) var.getTarget()).getSimpleName().equals("ext")) ) { + if((var.getTarget() instanceof J.Identifier && ((J.Identifier) var.getTarget()).getSimpleName().equals("ext")) || + (var.getTarget() instanceof J.FieldAccess && ((J.FieldAccess) var.getTarget()).getSimpleName().equals("ext")) ) { as = updateAssignment(as); } } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyConstraintToRule.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyConstraintToRule.java index b0867c41771..474fea6c5d8 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyConstraintToRule.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyConstraintToRule.java @@ -340,15 +340,15 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Integ private static boolean isInDependenciesBlock(Cursor cursor) { Cursor c = cursor.dropParentUntil(value -> - value == Cursor.ROOT_VALUE - || (value instanceof J.MethodInvocation && DEPENDENCIES_DSL_MATCHER.matches((J.MethodInvocation) value))); + value == Cursor.ROOT_VALUE || + (value instanceof J.MethodInvocation && DEPENDENCIES_DSL_MATCHER.matches((J.MethodInvocation) value))); return c.getValue() instanceof J.MethodInvocation; } private static boolean isEachDependency(J.MethodInvocation m) { - return "eachDependency".equals(m.getSimpleName()) - && (m.getSelect() instanceof J.Identifier - && "resolutionStrategy".equals(((J.Identifier) m.getSelect()).getSimpleName())); + return "eachDependency".equals(m.getSimpleName()) && + (m.getSelect() instanceof J.Identifier && + "resolutionStrategy".equals(((J.Identifier) m.getSelect()).getSimpleName())); } private static boolean predicateRelatesToGav(J.If iff, GroupArtifactVersionBecause groupArtifactVersion) { diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java index 88c5e68b977..0338bdc25d8 100755 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/DependencyUseMapNotation.java @@ -124,8 +124,8 @@ private J.MethodInvocation forGString(J.MethodInvocation m) { // Supporting all possible GString interpolations is impossible // Supporting all probable GString interpolations is difficult // This focuses on the most common case: When only the version number is interpolated - if (g.getStrings().size() != 2 || !(g.getStrings().get(0) instanceof J.Literal) - || !(g.getStrings().get(1) instanceof G.GString.Value)) { + if (g.getStrings().size() != 2 || !(g.getStrings().get(0) instanceof J.Literal) || + !(g.getStrings().get(1) instanceof G.GString.Value)) { return m; } J.Literal arg1 = (J.Literal)g.getStrings().get(0); diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java index ae1c4bb520c..ca7f2ec837d 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/RemoveDependency.java @@ -145,8 +145,8 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) //noinspection DataFlowIssue return maybeRemoveDependency(m); } else if (firstArgument instanceof J.MethodInvocation && - (((J.MethodInvocation) firstArgument).getSimpleName().equals("platform") - || ((J.MethodInvocation) firstArgument).getSimpleName().equals("enforcedPlatform"))) { + (((J.MethodInvocation) firstArgument).getSimpleName().equals("platform") || + ((J.MethodInvocation) firstArgument).getSimpleName().equals("enforcedPlatform"))) { J after = maybeRemoveDependency((J.MethodInvocation) firstArgument); if (after == null) { //noinspection DataFlowIssue diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateJavaCompatibility.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateJavaCompatibility.java index 5ce95859218..6543204360c 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateJavaCompatibility.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpdateJavaCompatibility.java @@ -186,8 +186,8 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) } } - return SearchResult.found(m, "Attempted to update to Java version to " + version - + " but was unsuccessful, please update manually"); + return SearchResult.found(m, "Attempted to update to Java version to " + version + + " but was unsuccessful, please update manually"); } if (sourceCompatibilityDsl.matches(m) || targetCompatibilityDsl.matches(m)) { @@ -208,8 +208,8 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) } } - return SearchResult.found(m, "Attempted to update to Java version to " + version - + " but was unsuccessful, please update manually"); + return SearchResult.found(m, "Attempted to update to Java version to " + version + + " but was unsuccessful, please update manually"); } return m; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index 671f45a89b8..9c71401d44d 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -434,9 +434,9 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution return arg; } Dependency dep = DependencyStringNotationConverter.parse(gav); - if (dep != null && dependencyMatcher.matches(dep.getGroupId(), dep.getArtifactId()) - && dep.getVersion() != null - && !dep.getVersion().startsWith("$")) { + if (dep != null && dependencyMatcher.matches(dep.getGroupId(), dep.getArtifactId()) && + dep.getVersion() != null && + !dep.getVersion().startsWith("$")) { Object scanResult = acc.gaToNewVersion.get(new GroupArtifact(dep.getGroupId(), dep.getArtifactId())); if (scanResult instanceof Exception) { getCursor().putMessage(UPDATE_VERSION_ERROR_KEY, scanResult); @@ -472,9 +472,9 @@ private J.MethodInvocation updateDependency(J.MethodInvocation method, Execution m = Markup.warn(m, err); } List depArgs = m.getArguments(); - if (depArgs.size() >= 3 && depArgs.get(0) instanceof G.MapEntry - && depArgs.get(1) instanceof G.MapEntry - && depArgs.get(2) instanceof G.MapEntry) { + if (depArgs.size() >= 3 && depArgs.get(0) instanceof G.MapEntry && + depArgs.get(1) instanceof G.MapEntry && + depArgs.get(2) instanceof G.MapEntry) { Expression groupValue = ((G.MapEntry) depArgs.get(0)).getValue(); Expression artifactValue = ((G.MapEntry) depArgs.get(1)).getValue(); if (!(groupValue instanceof J.Literal) || !(artifactValue instanceof J.Literal)) { diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddPluginVisitor.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddPluginVisitor.java index 6dc13d46ea7..1a22bc3d5f9 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddPluginVisitor.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddPluginVisitor.java @@ -154,10 +154,10 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Integ .orElseThrow(() -> new IllegalArgumentException("Could not parse as Gradle")); if (FindMethods.find(cu, "RewriteGradleProject plugins(..)").isEmpty() && FindMethods.find(cu, "RewriteSettings plugins(..)").isEmpty()) { - if (cu.getSourcePath().endsWith(Paths.get("settings.gradle")) - && !cu.getStatements().isEmpty() - && cu.getStatements().get(0) instanceof J.MethodInvocation - && ((J.MethodInvocation) cu.getStatements().get(0)).getSimpleName().equals("pluginManagement")) { + if (cu.getSourcePath().endsWith(Paths.get("settings.gradle")) && + !cu.getStatements().isEmpty() && + cu.getStatements().get(0) instanceof J.MethodInvocation && + ((J.MethodInvocation) cu.getStatements().get(0)).getSimpleName().equals("pluginManagement")) { return cu.withStatements(ListUtils.insert(cu.getStatements(), autoFormat(statement.withPrefix(Space.format("\n\n")), ctx, getCursor()), 1)); } else { int insertAtIdx = 0; diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java index 7176a720006..6337d228db0 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepository.java @@ -78,16 +78,16 @@ public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionCon statements.add(pluginManagement); } else { Statement statement = statements.get(0); - if (statement instanceof J.MethodInvocation - && ((J.MethodInvocation) statement).getSimpleName().equals("pluginManagement")) { + if (statement instanceof J.MethodInvocation && + ((J.MethodInvocation) statement).getSimpleName().equals("pluginManagement")) { J.MethodInvocation m = (J.MethodInvocation) statement; m = m.withArguments(ListUtils.mapFirst(m.getArguments(), arg -> { if (arg instanceof J.Lambda && ((J.Lambda) arg).getBody() instanceof J.Block) { J.Lambda lambda = (J.Lambda) arg; J.Block block = (J.Block) lambda.getBody(); return lambda.withBody(block.withStatements(ListUtils.map(block.getStatements(), statement2 -> { - if ((statement2 instanceof J.MethodInvocation && ((J.MethodInvocation) statement2).getSimpleName().equals("repositories")) - || (statement2 instanceof J.Return && ((J.Return) statement2).getExpression() instanceof J.MethodInvocation && ((J.MethodInvocation) ((J.Return) statement2).getExpression()).getSimpleName().equals("repositories"))) { + if ((statement2 instanceof J.MethodInvocation && ((J.MethodInvocation) statement2).getSimpleName().equals("repositories")) || + (statement2 instanceof J.Return && ((J.Return) statement2).getExpression() instanceof J.MethodInvocation && ((J.MethodInvocation) ((J.Return) statement2).getExpression()).getSimpleName().equals("repositories"))) { J.MethodInvocation m2 = (J.MethodInvocation) (statement2 instanceof J.Return ? ((J.Return) statement2).getExpression() : statement2); return m2.withArguments(ListUtils.mapFirst(m2.getArguments(), arg2 -> { if (arg2 instanceof J.Lambda && ((J.Lambda) arg2).getBody() instanceof J.Block) { diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/RemovePluginVisitor.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/RemovePluginVisitor.java index 9e50bf1a058..6e1b50c167b 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/RemovePluginVisitor.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/plugins/RemovePluginVisitor.java @@ -48,8 +48,8 @@ public J.Block visitBlock(J.Block block, ExecutionContext executionContext) { J.MethodInvocation m = getCursor().firstEnclosing(J.MethodInvocation.class); if (m != null && buildPluginsContainerMatcher.matches(m) || settingsPluginsContainerMatcher.matches(m)) { b = b.withStatements(ListUtils.map(b.getStatements(), statement -> { - if (!(statement instanceof J.MethodInvocation - || (statement instanceof J.Return && ((J.Return) statement).getExpression() instanceof J.MethodInvocation))) { + if (!(statement instanceof J.MethodInvocation || + (statement instanceof J.Return && ((J.Return) statement).getExpression() instanceof J.MethodInvocation))) { return statement; } @@ -59,24 +59,24 @@ public J.Block visitBlock(J.Block block, ExecutionContext executionContext) { return null; } } else if (buildPluginWithVersionMatcher.matches(m2) || settingsPluginWithVersionMatcher.matches(m2)) { - if (m2.getSelect() instanceof J.MethodInvocation - && ((J.MethodInvocation) m2.getSelect()).getArguments().get(0) instanceof J.Literal - && pluginId.equals(((J.Literal) ((J.MethodInvocation) m2.getSelect()).getArguments().get(0)).getValue())) { + if (m2.getSelect() instanceof J.MethodInvocation && + ((J.MethodInvocation) m2.getSelect()).getArguments().get(0) instanceof J.Literal && + pluginId.equals(((J.Literal) ((J.MethodInvocation) m2.getSelect()).getArguments().get(0)).getValue())) { return null; } } else if (buildPluginWithApplyMatcher.matches(m2) || settingsPluginWithApplyMatcher.matches(m2)) { if (buildPluginMatcher.matches(m2.getSelect()) || settingsPluginMatcher.matches(m2.getSelect())) { - if (m2.getSelect() instanceof J.MethodInvocation - && ((J.MethodInvocation) m2.getSelect()).getArguments().get(0) instanceof J.Literal - && pluginId.equals(((J.Literal) ((J.MethodInvocation) m2.getSelect()).getArguments().get(0)).getValue())) { + if (m2.getSelect() instanceof J.MethodInvocation && + ((J.MethodInvocation) m2.getSelect()).getArguments().get(0) instanceof J.Literal && + pluginId.equals(((J.Literal) ((J.MethodInvocation) m2.getSelect()).getArguments().get(0)).getValue())) { return null; } } else if (buildPluginWithVersionMatcher.matches(m2.getSelect()) || settingsPluginWithVersionMatcher.matches(m2.getSelect())) { - if (m2.getSelect() instanceof J.MethodInvocation - && (buildPluginMatcher.matches(((J.MethodInvocation) m2.getSelect()).getSelect()) || settingsPluginMatcher.matches(((J.MethodInvocation) m2.getSelect()).getSelect()))) { - if (((J.MethodInvocation) m2.getSelect()).getSelect() instanceof J.MethodInvocation - && ((J.MethodInvocation) ((J.MethodInvocation) m2.getSelect()).getSelect()).getArguments().get(0) instanceof J.Literal - && pluginId.equals(((J.Literal) ((J.MethodInvocation) ((J.MethodInvocation) m2.getSelect()).getSelect()).getArguments().get(0)).getValue())) { + if (m2.getSelect() instanceof J.MethodInvocation && + (buildPluginMatcher.matches(((J.MethodInvocation) m2.getSelect()).getSelect()) || settingsPluginMatcher.matches(((J.MethodInvocation) m2.getSelect()).getSelect()))) { + if (((J.MethodInvocation) m2.getSelect()).getSelect() instanceof J.MethodInvocation && + ((J.MethodInvocation) ((J.MethodInvocation) m2.getSelect()).getSelect()).getArguments().get(0) instanceof J.Literal && + pluginId.equals(((J.Literal) ((J.MethodInvocation) ((J.MethodInvocation) m2.getSelect()).getSelect()).getArguments().get(0)).getValue())) { return null; } } @@ -95,9 +95,9 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu J.MethodInvocation m = super.visitMethodInvocation(method, executionContext); if (buildPluginsContainerMatcher.matches(m) || settingsPluginsContainerMatcher.matches(m)) { - if (m.getArguments().get(0) instanceof J.Lambda - && ((J.Lambda) m.getArguments().get(0)).getBody() instanceof J.Block - && ((J.Block) ((J.Lambda) m.getArguments().get(0)).getBody()).getStatements().isEmpty()) { + if (m.getArguments().get(0) instanceof J.Lambda && + ((J.Lambda) m.getArguments().get(0)).getBody() instanceof J.Block && + ((J.Block) ((J.Lambda) m.getArguments().get(0)).getBody()).getStatements().isEmpty()) { //noinspection DataFlowIssue return null; } diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/search/FindRepository.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/search/FindRepository.java index c72774bd282..d8aa5750bc0 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/search/FindRepository.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/search/FindRepository.java @@ -75,8 +75,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu return new RepositoryVisitor().visitMethodInvocation(method, ctx); } else { boolean isPluginBlock = pluginManagementMatcher.matches(method) || buildscriptMatcher.matches(method); - if ((purpose == Purpose.Project && !isPluginBlock) - || (purpose == Purpose.Plugin && isPluginBlock)) { + if ((purpose == Purpose.Project && !isPluginBlock) || + (purpose == Purpose.Plugin && isPluginBlock)) { return new RepositoryVisitor().visitMethodInvocation(method, ctx); } } @@ -124,15 +124,15 @@ private boolean urlMatches(J.MethodInvocation m, String url) { for (Statement statement : block.getStatements()) { if (statement instanceof J.Assignment || (statement instanceof J.Return && ((J.Return) statement).getExpression() instanceof J.Assignment)) { J.Assignment assignment = (J.Assignment) (statement instanceof J.Return ? ((J.Return) statement).getExpression() : statement); - if (assignment.getVariable() instanceof J.Identifier - && "url".equals(((J.Identifier) assignment.getVariable()).getSimpleName())) { - if (assignment.getAssignment() instanceof J.Literal - && url.equals(((J.Literal) assignment.getAssignment()).getValue())) { + if (assignment.getVariable() instanceof J.Identifier && + "url".equals(((J.Identifier) assignment.getVariable()).getSimpleName())) { + if (assignment.getAssignment() instanceof J.Literal && + url.equals(((J.Literal) assignment.getAssignment()).getValue())) { return true; - } else if (assignment.getAssignment() instanceof J.MethodInvocation - && ((J.MethodInvocation) assignment.getAssignment()).getSimpleName().equals("uri") - && ((J.MethodInvocation) assignment.getAssignment()).getArguments().get(0) instanceof J.Literal - && url.equals(((J.Literal) ((J.MethodInvocation) assignment.getAssignment()).getArguments().get(0)).getValue())) { + } else if (assignment.getAssignment() instanceof J.MethodInvocation && + ((J.MethodInvocation) assignment.getAssignment()).getSimpleName().equals("uri") && + ((J.MethodInvocation) assignment.getAssignment()).getArguments().get(0) instanceof J.Literal && + url.equals(((J.Literal) ((J.MethodInvocation) assignment.getAssignment()).getArguments().get(0)).getValue())) { return true; } else if (assignment.getAssignment() instanceof G.GString) { String valueSource = assignment.getAssignment().withPrefix(Space.EMPTY).printTrimmed(new GroovyPrinter<>()); @@ -143,13 +143,13 @@ private boolean urlMatches(J.MethodInvocation m, String url) { } else if (statement instanceof J.MethodInvocation || (statement instanceof J.Return && ((J.Return) statement).getExpression() instanceof J.MethodInvocation)) { J.MethodInvocation m1 = (J.MethodInvocation) (statement instanceof J.Return ? ((J.Return) statement).getExpression() : statement); if (m1.getSimpleName().equals("setUrl") || m1.getSimpleName().equals("url")) { - if (m1.getArguments().get(0) instanceof J.Literal - && url.equals(((J.Literal) m1.getArguments().get(0)).getValue())) { + if (m1.getArguments().get(0) instanceof J.Literal && + url.equals(((J.Literal) m1.getArguments().get(0)).getValue())) { return true; - } else if (m1.getArguments().get(0) instanceof J.MethodInvocation - && ((J.MethodInvocation) m1.getArguments().get(0)).getSimpleName().equals("uri") - && ((J.MethodInvocation) m1.getArguments().get(0)).getArguments().get(0) instanceof J.Literal - && url.equals(((J.Literal) ((J.MethodInvocation) m1.getArguments().get(0)).getArguments().get(0)).getValue())) { + } else if (m1.getArguments().get(0) instanceof J.MethodInvocation && + ((J.MethodInvocation) m1.getArguments().get(0)).getSimpleName().equals("uri") && + ((J.MethodInvocation) m1.getArguments().get(0)).getArguments().get(0) instanceof J.Literal && + url.equals(((J.Literal) ((J.MethodInvocation) m1.getArguments().get(0)).getArguments().get(0)).getValue())) { return true; } else if (m1.getArguments().get(0) instanceof G.GString) { G.GString value = (G.GString) m1.getArguments().get(0); diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/ChangeStringLiteral.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/ChangeStringLiteral.java index e0f5e46c3ec..605224c0ec4 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/ChangeStringLiteral.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/util/ChangeStringLiteral.java @@ -29,8 +29,8 @@ public static J.Literal withStringValue(J.Literal l, String newValue) { return l; } String valueSource = l.getValueSource(); - String delimiter = (valueSource == null) ? "'" - : valueSource.substring(0, valueSource.indexOf(oldValue)); + String delimiter = (valueSource == null) ? "'" : + valueSource.substring(0, valueSource.indexOf(oldValue)); return l.withValue(newValue).withValueSource(delimiter + newValue + delimiter); } } diff --git a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java index 8060e68c238..a1a0f5ed465 100644 --- a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java +++ b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java @@ -149,10 +149,10 @@ public G.CompilationUnit visit(SourceUnit unit, ModuleNode ast) throws GroovyPar } for (ClassNode aClass : ast.getClasses()) { - if (aClass.getSuperClass() == null - || !("groovy.lang.Script".equals(aClass.getSuperClass().getName()) - || "RewriteGradleProject".equals(aClass.getSuperClass().getName()) - || "RewriteSettings".equals(aClass.getSuperClass().getName()))) { + if (aClass.getSuperClass() == null || + !("groovy.lang.Script".equals(aClass.getSuperClass().getName()) || + "RewriteGradleProject".equals(aClass.getSuperClass().getName()) || + "RewriteSettings".equals(aClass.getSuperClass().getName()))) { sortedByPosition.computeIfAbsent(pos(aClass), i -> new ArrayList<>()).add(aClass); } } @@ -673,10 +673,10 @@ public void visitArgumentlistExpression(ArgumentListExpression expression) { // If the first parameter to a function is a Map, then groovy allows "named parameters" style invocations, see: // https://docs.groovy-lang.org/latest/html/documentation/#_named_parameters_2 // When named parameters are in use they may appear before, after, or intermixed with any positional arguments - if (unparsedArgs.size() > 1 && unparsedArgs.get(0) instanceof MapExpression - && (unparsedArgs.get(0).getLastLineNumber() > unparsedArgs.get(1).getLastLineNumber() - || (unparsedArgs.get(0).getLastLineNumber() == unparsedArgs.get(1).getLastLineNumber() - && unparsedArgs.get(0).getLastColumnNumber() > unparsedArgs.get(1).getLastColumnNumber()))) { + if (unparsedArgs.size() > 1 && unparsedArgs.get(0) instanceof MapExpression && + (unparsedArgs.get(0).getLastLineNumber() > unparsedArgs.get(1).getLastLineNumber() || + (unparsedArgs.get(0).getLastLineNumber() == unparsedArgs.get(1).getLastLineNumber() && + unparsedArgs.get(0).getLastColumnNumber() > unparsedArgs.get(1).getLastColumnNumber()))) { // Figure out the source-code ordering of the expressions MapExpression namedArgExpressions = (MapExpression) unparsedArgs.get(0); @@ -968,9 +968,9 @@ public void visitCatchStatement(CatchStatement node) { Space paramPrefix = whitespace(); // Groovy allows catch variables to omit their type, shorthand for being of type java.lang.Exception // Can't use isSynthetic() here because groovy doesn't record the line number on the Parameter - if ("java.lang.Exception".equals(param.getType().getName()) - && !source.startsWith("Exception", cursor) - && !source.startsWith("java.lang.Exception", cursor)) { + if ("java.lang.Exception".equals(param.getType().getName()) && + !source.startsWith("Exception", cursor) && + !source.startsWith("java.lang.Exception", cursor)) { paramType = new J.Identifier(randomId(), paramPrefix, Markers.EMPTY, emptyList(), "", JavaType.ShallowClass.build("java.lang.Exception"), null); } else { @@ -1016,9 +1016,9 @@ public void visitCaseStatement(CaseStatement statement) { J.Case.Type.Statement, null, JContainer.build(singletonList(JRightPadded.build(visit(statement.getExpression())))), - statement.getCode() instanceof EmptyStatement - ? JContainer.build(sourceBefore(":"), convertStatements(emptyList()), Markers.EMPTY) - : JContainer.build(sourceBefore(":"), convertStatements(((BlockStatement) statement.getCode()).getStatements()), Markers.EMPTY) + statement.getCode() instanceof EmptyStatement ? + JContainer.build(sourceBefore(":"), convertStatements(emptyList()), Markers.EMPTY) : + JContainer.build(sourceBefore(":"), convertStatements(((BlockStatement) statement.getCode()).getStatements()), Markers.EMPTY) , null) ); } @@ -1610,8 +1610,8 @@ public void visitMethodCallExpression(MethodCallExpression call) { MethodNode methodNode = (MethodNode) call.getNodeMetaData().get(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); JavaType.Method methodType = null; - if (methodNode == null && call.getObjectExpression() instanceof VariableExpression - && ((VariableExpression) call.getObjectExpression()).getAccessedVariable() != null) { + if (methodNode == null && call.getObjectExpression() instanceof VariableExpression && + ((VariableExpression) call.getObjectExpression()).getAccessedVariable() != null) { // Groovy doesn't know what kind of object this method is being invoked on // But if this invocation is inside a Closure we may have already enriched its parameters with types from the static type checker // Use any such type information to attempt to find a matching method diff --git a/rewrite-hcl/src/main/java/org/openrewrite/hcl/search/FindAndReplaceLiteral.java b/rewrite-hcl/src/main/java/org/openrewrite/hcl/search/FindAndReplaceLiteral.java index 98e7075adf4..08b9f594f4b 100644 --- a/rewrite-hcl/src/main/java/org/openrewrite/hcl/search/FindAndReplaceLiteral.java +++ b/rewrite-hcl/src/main/java/org/openrewrite/hcl/search/FindAndReplaceLiteral.java @@ -43,8 +43,8 @@ public String getDisplayName() { @Override public String getDescription() { - return "Find and replace literal values in HCL files. This recipe parses the source files on which it runs as HCL, " - + "meaning you can execute HCL language-specific recipes before and after this recipe in a single recipe run."; + return "Find and replace literal values in HCL files. This recipe parses the source files on which it runs as HCL, " + + "meaning you can execute HCL language-specific recipes before and after this recipe in a single recipe run."; } @Option(displayName = "Find", description = "The literal to find (and replace)", example = "blacklist") diff --git a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserInputFileObject.java b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserInputFileObject.java index 589ba611c16..7c121b539dd 100644 --- a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserInputFileObject.java +++ b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserInputFileObject.java @@ -111,8 +111,8 @@ public Kind getKind() { @Override public boolean isNameCompatible(String simpleName, Kind kind) { String baseName = simpleName + kind.extension; - return kind.equals(getKind()) - && path.getFileName().toString().equals(baseName); + return kind.equals(getKind()) && + path.getFileName().toString().equals(baseName); } @Override diff --git a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java index 100e3763102..75d9a2509f6 100644 --- a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java +++ b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17Parser.java @@ -331,8 +331,8 @@ public String inferBinaryName(Location location, JavaFileObject file) { public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { if (StandardLocation.CLASS_PATH.equals(location)) { Iterable listed = super.list(location, packageName, kinds, recurse); - return classByteClasspath.isEmpty() ? listed - : Stream.concat(classByteClasspath.stream() + return classByteClasspath.isEmpty() ? listed : + Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); diff --git a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserInputFileObject.java b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserInputFileObject.java index 6708943d17b..c6bdfa9895e 100644 --- a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserInputFileObject.java +++ b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserInputFileObject.java @@ -111,8 +111,8 @@ public Kind getKind() { @Override public boolean isNameCompatible(String simpleName, Kind kind) { String baseName = simpleName + kind.extension; - return kind.equals(getKind()) - && path.getFileName().toString().equals(baseName); + return kind.equals(getKind()) && + path.getFileName().toString().equals(baseName); } @Override diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java index 2fda7bc1e06..823e2103f10 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21Parser.java @@ -331,8 +331,8 @@ public String inferBinaryName(Location location, JavaFileObject file) { public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { if (StandardLocation.CLASS_PATH.equals(location)) { Iterable listed = super.list(location, packageName, kinds, recurse); - return classByteClasspath.isEmpty() ? listed - : Stream.concat(classByteClasspath.stream() + return classByteClasspath.isEmpty() ? listed : + Stream.concat(classByteClasspath.stream() .filter(jfo -> jfo.getPackage().equals(packageName)), StreamSupport.stream(listed.spliterator(), false) ).collect(toList()); diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserInputFileObject.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserInputFileObject.java index 2c7def892da..e2035dfcf71 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserInputFileObject.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserInputFileObject.java @@ -111,8 +111,8 @@ public Kind getKind() { @Override public boolean isNameCompatible(String simpleName, Kind kind) { String baseName = simpleName + kind.extension; - return kind.equals(getKind()) - && path.getFileName().toString().equals(baseName); + return kind.equals(getKind()) && + path.getFileName().toString().equals(baseName); } @Override diff --git a/rewrite-java-8/src/main/java/org/openrewrite/java/Java8ParserInputFileObject.java b/rewrite-java-8/src/main/java/org/openrewrite/java/Java8ParserInputFileObject.java index 6b8758f165e..a088d1811fc 100644 --- a/rewrite-java-8/src/main/java/org/openrewrite/java/Java8ParserInputFileObject.java +++ b/rewrite-java-8/src/main/java/org/openrewrite/java/Java8ParserInputFileObject.java @@ -110,8 +110,8 @@ public Kind getKind() { @Override public boolean isNameCompatible(String simpleName, Kind kind) { String baseName = simpleName + kind.extension; - return kind.equals(getKind()) - && path.getFileName().toString().equals(baseName); + return kind.equals(getKind()) && + path.getFileName().toString().equals(baseName); } @Override diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateMatchTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateMatchTest.java index e1f1186bde5..34c1fb4a23b 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateMatchTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateMatchTest.java @@ -136,9 +136,9 @@ void matchNamedParameterMultipleReferences() { spec -> spec.recipe(toRecipe(() -> new JavaVisitor<>() { @Override public J visitBinary(J.Binary binary, ExecutionContext ctx) { - return JavaTemplate.matches("#{i:any(int)} == 1 && #{i} == #{j:any(int)}", getCursor()) - ? SearchResult.found(binary) - : super.visitBinary(binary, ctx); + return JavaTemplate.matches("#{i:any(int)} == 1 && #{i} == #{j:any(int)}", getCursor()) ? + SearchResult.found(binary) : + super.visitBinary(binary, ctx); } })), java( diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateTest4Test.java b/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateTest4Test.java index 73affde6a62..eda98fda77e 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateTest4Test.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/JavaTemplateTest4Test.java @@ -62,10 +62,10 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex .as("Changing the method's parameters should have resulted in the first parameter's type being 'int'") .isEqualTo(JavaType.Primitive.Int); assertThat(type.getParameterTypes().get(1)) - .matches(jt -> jt instanceof JavaType.Parameterized - && ((JavaType.Parameterized) jt).getType().getFullyQualifiedName().equals("java.util.List") - && ((JavaType.Parameterized) jt).getTypeParameters().size() == 1 - && TypeUtils.asFullyQualified(((JavaType.Parameterized) jt).getTypeParameters().get(0)).getFullyQualifiedName().equals("java.lang.String"), + .matches(jt -> jt instanceof JavaType.Parameterized && + ((JavaType.Parameterized) jt).getType().getFullyQualifiedName().equals("java.util.List") && + ((JavaType.Parameterized) jt).getTypeParameters().size() == 1 && + TypeUtils.asFullyQualified(((JavaType.Parameterized) jt).getTypeParameters().get(0)).getFullyQualifiedName().equals("java.lang.String"), "Changing the method's parameters should have resulted in the second parameter's type being 'List'" ); assertThat(m.getName().getType()).isEqualTo(type); diff --git a/rewrite-java-test/src/test/resources/dataflow-functional-tests/ArchiveAnalyzer.java b/rewrite-java-test/src/test/resources/dataflow-functional-tests/ArchiveAnalyzer.java index fd2b66b97ae..6722c2063b9 100644 --- a/rewrite-java-test/src/test/resources/dataflow-functional-tests/ArchiveAnalyzer.java +++ b/rewrite-java-test/src/test/resources/dataflow-functional-tests/ArchiveAnalyzer.java @@ -210,8 +210,8 @@ public void closeAnalyzer() throws Exception { if (!success && tempFileLocation.exists()) { final String[] l = tempFileLocation.list(); if (l != null && l.length > 0) { - LOGGER.warn("Failed to delete the Archive Analyzer's temporary files from `{}`, " - + "see the log for more details", tempFileLocation.toString()); + LOGGER.warn("Failed to delete the Archive Analyzer's temporary files from `{}`, " + + "see the log for more details", tempFileLocation.toString()); } } } @@ -511,14 +511,14 @@ private void ensureReadableJar(final String archiveExt, BufferedInputStream in) in.mark(7); final byte[] b = new byte[7]; final int read = in.read(b); - if (read == 7 - && b[0] == '#' - && b[1] == '!' - && b[2] == '/' - && b[3] == 'b' - && b[4] == 'i' - && b[5] == 'n' - && b[6] == '/') { + if (read == 7 && + b[0] == '#' && + b[1] == '!' && + b[2] == '/' && + b[3] == 'b' && + b[4] == 'i' && + b[5] == 'n' && + b[6] == '/') { boolean stillLooking = true; int chr; int nxtChr; @@ -646,8 +646,8 @@ private boolean isZipFileActuallyJarFile(Dependency dependency) { ZipFile zip = null; try { zip = new ZipFile(dependency.getActualFilePath()); - if (zip.getEntry("META-INF/MANIFEST.MF") != null - || zip.getEntry("META-INF/maven") != null) { + if (zip.getEntry("META-INF/MANIFEST.MF") != null || + zip.getEntry("META-INF/maven") != null) { final Enumeration entries = zip.getEntries(); while (entries.hasMoreElements()) { final ZipArchiveEntry entry = entries.nextElement(); diff --git a/rewrite-java-test/src/test/resources/dataflow-functional-tests/FileUtils.java b/rewrite-java-test/src/test/resources/dataflow-functional-tests/FileUtils.java index 2976e2f06e4..aae7cb22995 100644 --- a/rewrite-java-test/src/test/resources/dataflow-functional-tests/FileUtils.java +++ b/rewrite-java-test/src/test/resources/dataflow-functional-tests/FileUtils.java @@ -1719,10 +1719,10 @@ public static void forceMkdir(File directory) throws IOException { if (directory.exists()) { if (!directory.isDirectory()) { String message = - "File " - + directory - + " exists and is " - + "not a directory. Unable to create directory."; + "File " + + directory + + " exists and is " + + "not a directory. Unable to create directory."; throw new IOException(message); } } else { @@ -1823,8 +1823,8 @@ public static boolean isFileNewer(File file, File reference) { throw new IllegalArgumentException("No specified reference file"); } if (!reference.exists()) { - throw new IllegalArgumentException("The reference file '" - + reference + "' doesn't exist"); + throw new IllegalArgumentException("The reference file '" + + reference + "' doesn't exist"); } return isFileNewer(file, reference.lastModified()); } @@ -1890,8 +1890,8 @@ public static boolean isFileOlder(File file, File reference) { throw new IllegalArgumentException("No specified reference file"); } if (!reference.exists()) { - throw new IllegalArgumentException("The reference file '" - + reference + "' doesn't exist"); + throw new IllegalArgumentException("The reference file '" + + reference + "' doesn't exist"); } return isFileOlder(file, reference.lastModified()); } diff --git a/rewrite-java-test/src/test/resources/dataflow-functional-tests/GitCommandLineUtils.java b/rewrite-java-test/src/test/resources/dataflow-functional-tests/GitCommandLineUtils.java index 723adc93ffd..858517a7366 100644 --- a/rewrite-java-test/src/test/resources/dataflow-functional-tests/GitCommandLineUtils.java +++ b/rewrite-java-test/src/test/resources/dataflow-functional-tests/GitCommandLineUtils.java @@ -96,8 +96,8 @@ public static void addTarget( Commandline cl, List files ) } catch ( IOException ex ) { - throw new IllegalArgumentException( "Could not get canonical paths for workingDirectory = " - + workingDirectory + " or files=" + files, ex ); + throw new IllegalArgumentException( "Could not get canonical paths for workingDirectory = " + + workingDirectory + " or files=" + files, ex ); } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java b/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java index d32a50a1ed9..a3996085614 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/Assertions.java @@ -60,8 +60,8 @@ public static SourceFile validateTypes(SourceFile source, TypeValidation typeVal } private static void assertValidTypes(TypeValidation typeValidation, J sf) { - if (typeValidation.identifiers() || typeValidation.methodInvocations() || typeValidation.methodDeclarations() || typeValidation.classDeclarations() - || typeValidation.constructorInvocations()) { + if (typeValidation.identifiers() || typeValidation.methodInvocations() || typeValidation.methodDeclarations() || typeValidation.classDeclarations() || + typeValidation.constructorInvocations()) { List missingTypeResults = FindMissingTypes.findMissingTypes(sf); missingTypeResults = missingTypeResults.stream() .filter(missingType -> { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java index 3977032dafa..3a91450d242 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangePackage.java @@ -337,9 +337,9 @@ private boolean isTargetFullyQualifiedType(JavaType.@Nullable FullyQualified fq) } private boolean isTargetRecursivePackageName(String packageName) { - return (recursive == null || recursive) - && packageName.startsWith(oldPackageName + ".") - && !packageName.startsWith(newPackageName); + return (recursive == null || recursive) && + packageName.startsWith(oldPackageName + ".") && + !packageName.startsWith(newPackageName); } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java b/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java index 7f83e218494..d4c278783cc 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ChangeStaticFieldToMethod.java @@ -127,8 +127,8 @@ private J useNewMethod(TypeTree tree) { method = (J.MethodInvocation) newArray.getInitializer().get(0); } if (method == null || method.getMethodType() == null) { - throw new IllegalArgumentException("Error while changing a static field to a method. The generated template using a the new class [" - + newClass + "] and the method [" + newMethodName + "] resulted in a null method type."); + throw new IllegalArgumentException("Error while changing a static field to a method. The generated template using a the new class [" + + newClass + "] and the method [" + newMethodName + "] resulted in a null method type."); } if (tree.getType() != null) { JavaType.Method mt = method.getMethodType().withReturnType(tree.getType()); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ImplementInterface.java b/rewrite-java/src/main/java/org/openrewrite/java/ImplementInterface.java index 4423eea4bb9..7400a7814da 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ImplementInterface.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ImplementInterface.java @@ -40,8 +40,8 @@ public ImplementInterface(J.ClassDeclaration scope, JavaType.FullyQualified inte public ImplementInterface(J.ClassDeclaration scope, String interface_, @Nullable List typeParameters) { this(scope, ListUtils.nullIfEmpty(typeParameters) != null ? - new JavaType.Parameterized(null, JavaType.ShallowClass.build(interface_), typeParameters.stream().map(Expression::getType).collect(Collectors.toList())) - : JavaType.ShallowClass.build(interface_), + new JavaType.Parameterized(null, JavaType.ShallowClass.build(interface_), typeParameters.stream().map(Expression::getType).collect(Collectors.toList())) : + JavaType.ShallowClass.build(interface_), typeParameters ); } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java b/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java index d65f98c917a..41a1b56b905 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/MethodMatcher.java @@ -252,8 +252,8 @@ public boolean matches(J.MethodDeclaration method, J.ClassDeclaration enclosing) // aspectJUtils does not support matching classes separated by packages. // [^.]* is the product of a fully wild card match for a method. `* foo()` - boolean matchesTargetType = (targetTypePattern != null && "[^.]*".equals(targetTypePattern.pattern())) - || matchesTargetType(enclosing.getType()); + boolean matchesTargetType = (targetTypePattern != null && "[^.]*".equals(targetTypePattern.pattern())) || + matchesTargetType(enclosing.getType()); if (!matchesTargetType) { return false; } @@ -279,8 +279,8 @@ public boolean matches(J.MethodDeclaration method, J.NewClass enclosing) { // aspectJUtils does not support matching classes separated by packages. // [^.]* is the product of a fully wild card match for a method. `* foo()` - boolean matchesTargetType = (targetTypePattern != null && "[^.]*".equals(targetTypePattern.pattern())) - || TypeUtils.isAssignableTo(targetType, enclosing.getType()); + boolean matchesTargetType = (targetTypePattern != null && "[^.]*".equals(targetTypePattern.pattern())) || + TypeUtils.isAssignableTo(targetType, enclosing.getType()); if (!matchesTargetType) { return false; } @@ -337,9 +337,9 @@ private boolean matchesAllowingUnknownTypes(J.MethodInvocation method) { return false; } - if (method.getSelect() != null - && method.getSelect() instanceof J.Identifier - && !matchesSelectBySimpleNameAlone(((J.Identifier) method.getSelect()))) { + if (method.getSelect() != null && + method.getSelect() instanceof J.Identifier && + !matchesSelectBySimpleNameAlone(((J.Identifier) method.getSelect()))) { return false; } @@ -366,9 +366,9 @@ private String argumentsFromExpressionTypes(J.MethodInvocation method) { StringJoiner joiner = new StringJoiner(","); for (Expression expr : method.getArguments()) { final JavaType exprType = expr.getType(); - String s = exprType == null - ? JavaType.Unknown.getInstance().getFullyQualifiedName() - : typePattern(exprType); + String s = exprType == null ? + JavaType.Unknown.getInstance().getFullyQualifiedName() : + typePattern(exprType); joiner.add(s); } return joiner.toString(); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/QualifyThisVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/QualifyThisVisitor.java index 4adcda701af..3ccb7c0eeba 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/QualifyThisVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/QualifyThisVisitor.java @@ -29,9 +29,9 @@ public class QualifyThisVisitor extends JavaVisitor { @Override public J visitIdentifier(J.Identifier ident, ExecutionContext executionContext) { - if (ident.getSimpleName().equals("this") - && !isAlreadyQualified(ident) - && ident.getType() instanceof JavaType.Class) { + if (ident.getSimpleName().equals("this") && + !isAlreadyQualified(ident) && + ident.getType() instanceof JavaType.Class) { JavaType.Class type = (JavaType.Class) ident.getType(); return new J.FieldAccess( Tree.randomId(), diff --git a/rewrite-java/src/main/java/org/openrewrite/java/RemoveImport.java b/rewrite-java/src/main/java/org/openrewrite/java/RemoveImport.java index b0a33af0dfc..22787f86897 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/RemoveImport.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/RemoveImport.java @@ -75,8 +75,8 @@ public RemoveImport(String type, boolean force) { for (JavaType.Variable variable : cu.getTypesInUse().getVariables()) { JavaType.FullyQualified fq = TypeUtils.asFullyQualified(variable.getOwner()); - if (fq != null && (TypeUtils.fullyQualifiedNamesAreEqual(fq.getFullyQualifiedName(), type) - || TypeUtils.fullyQualifiedNamesAreEqual(fq.getFullyQualifiedName(), owner))) { + if (fq != null && (TypeUtils.fullyQualifiedNamesAreEqual(fq.getFullyQualifiedName(), type) || + TypeUtils.fullyQualifiedNamesAreEqual(fq.getFullyQualifiedName(), owner))) { methodsAndFieldsUsed.add(variable.getName()); } } @@ -101,8 +101,8 @@ public RemoveImport(String type, boolean force) { JavaType.FullyQualified fullyQualified = (JavaType.FullyQualified) javaType; if (TypeUtils.fullyQualifiedNamesAreEqual(fullyQualified.getFullyQualifiedName(), type)) { typeUsed = true; - } else if (TypeUtils.fullyQualifiedNamesAreEqual(fullyQualified.getFullyQualifiedName(), owner) - || TypeUtils.fullyQualifiedNamesAreEqual(fullyQualified.getPackageName(), owner)) { + } else if (TypeUtils.fullyQualifiedNamesAreEqual(fullyQualified.getFullyQualifiedName(), owner) || + TypeUtils.fullyQualifiedNamesAreEqual(fullyQualified.getPackageName(), owner)) { if (!originalImports.contains(fullyQualified.getFullyQualifiedName().replace("$", "."))) { otherTypesInPackageUsed.add(fullyQualified.getClassName()); } @@ -132,8 +132,8 @@ public RemoveImport(String type, boolean force) { // e.g. remove java.util.Collections.emptySet when type is java.util.Collections.emptySet spaceForNextImport.set(import_.getPrefix()); return null; - } else if ("*".equals(imported) && (TypeUtils.fullyQualifiedNamesAreEqual(typeName, type) - || TypeUtils.fullyQualifiedNamesAreEqual(typeName + type.substring(type.lastIndexOf('.')), type))) { + } else if ("*".equals(imported) && (TypeUtils.fullyQualifiedNamesAreEqual(typeName, type) || + TypeUtils.fullyQualifiedNamesAreEqual(typeName + type.substring(type.lastIndexOf('.')), type))) { if (methodsAndFieldsUsed.isEmpty() && otherMethodsAndFieldsInTypeUsed.isEmpty()) { spaceForNextImport.set(import_.getPrefix()); return null; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java b/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java index a765e31e9d8..9d393883520 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferences.java @@ -44,9 +44,9 @@ public String getDisplayName() { @Override public String getDescription() { - return "Any fully qualified references to Java types will be replaced with corresponding simple " - + "names and import statements, provided that it doesn't result in " - + "any conflicts with other imports or types declared in the local compilation unit."; + return "Any fully qualified references to Java types will be replaced with corresponding simple " + + "names and import statements, provided that it doesn't result in " + + "any conflicts with other imports or types declared in the local compilation unit."; } @Override diff --git a/rewrite-java/src/main/java/org/openrewrite/java/TreeVisitingPrinter.java b/rewrite-java/src/main/java/org/openrewrite/java/TreeVisitingPrinter.java index 1ec21a30afe..7c442b1004e 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/TreeVisitingPrinter.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/TreeVisitingPrinter.java @@ -269,9 +269,9 @@ private static String printComments(List comments) { } // print current visiting element - String typeName = tree instanceof J - ? tree.getClass().getCanonicalName().substring(tree.getClass().getPackage().getName().length() + 1) - : tree.getClass().getCanonicalName(); + String typeName = tree instanceof J ? + tree.getClass().getCanonicalName().substring(tree.getClass().getPackage().getName().length() + 1) : + tree.getClass().getCanonicalName(); if (skipUnvisitedElement) { boolean leftPadded = diffPos >= 0; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java b/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java index 50808128be1..7934d78b055 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/cleanup/SimplifyBooleanExpressionVisitor.java @@ -231,9 +231,9 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext execu J j = super.visitMethodInvocation(method, executionContext); J.MethodInvocation asMethod = (J.MethodInvocation) j; Expression select = asMethod.getSelect(); - if (isEmpty.matches(asMethod) - && select instanceof J.Literal - && select.getType() == JavaType.Primitive.String) { + if (isEmpty.matches(asMethod) && + select instanceof J.Literal && + select.getType() == JavaType.Primitive.String) { return booleanLiteral(method, J.Literal.isLiteralValue(select, "")); } return j; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/template/BlockStatementTemplateGenerator.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/template/BlockStatementTemplateGenerator.java index 92ce7c4fb03..3be2c6e2b0b 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/template/BlockStatementTemplateGenerator.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/template/BlockStatementTemplateGenerator.java @@ -222,9 +222,9 @@ protected void contextFreeTemplate(Cursor cursor, J j, StringBuilder before, Str before.insert(0, "class Template {\n"); before.append("Object o = "); after.append(";\n}"); - } else if ((j instanceof J.MethodDeclaration || j instanceof J.VariableDeclarations || j instanceof J.Block || j instanceof J.ClassDeclaration) - && cursor.getValue() instanceof J.Block - && (cursor.getParent().getValue() instanceof J.ClassDeclaration || cursor.getParent().getValue() instanceof J.NewClass)) { + } else if ((j instanceof J.MethodDeclaration || j instanceof J.VariableDeclarations || j instanceof J.Block || j instanceof J.ClassDeclaration) && + cursor.getValue() instanceof J.Block && + (cursor.getParent().getValue() instanceof J.ClassDeclaration || cursor.getParent().getValue() instanceof J.NewClass)) { before.insert(0, "class Template {\n"); after.append("\n}"); } else if (j instanceof J.ClassDeclaration) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/template/PatternVariables.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/template/PatternVariables.java index 96ffa084fec..806ca787725 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/template/PatternVariables.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/template/PatternVariables.java @@ -197,12 +197,12 @@ private static boolean neverCompletesNormally0(@Nullable Statement statement, Se return true; } else if (statement instanceof J.Break) { J.Break breakStatement = (J.Break) statement; - return breakStatement.getLabel() != null && !labelsToIgnore.contains(breakStatement.getLabel().getSimpleName()) - || breakStatement.getLabel() == null && !labelsToIgnore.contains(DEFAULT_LABEL); + return breakStatement.getLabel() != null && !labelsToIgnore.contains(breakStatement.getLabel().getSimpleName()) || + breakStatement.getLabel() == null && !labelsToIgnore.contains(DEFAULT_LABEL); } else if (statement instanceof J.Continue) { J.Continue continueStatement = (J.Continue) statement; - return continueStatement.getLabel() != null && !labelsToIgnore.contains(continueStatement.getLabel().getSimpleName()) - || continueStatement.getLabel() == null && !labelsToIgnore.contains(DEFAULT_LABEL); + return continueStatement.getLabel() != null && !labelsToIgnore.contains(continueStatement.getLabel().getSimpleName()) || + continueStatement.getLabel() == null && !labelsToIgnore.contains(DEFAULT_LABEL); } else if (statement instanceof J.Block) { return neverCompletesNormally0(getLastStatement(statement), labelsToIgnore); } else if (statement instanceof Loop) { @@ -210,9 +210,9 @@ private static boolean neverCompletesNormally0(@Nullable Statement statement, Se return neverCompletesNormallyIgnoringLabel(loop.getBody(), DEFAULT_LABEL, labelsToIgnore); } else if (statement instanceof J.If) { J.If if_ = (J.If) statement; - return if_.getElsePart() != null - && neverCompletesNormally0(if_.getThenPart(), labelsToIgnore) - && neverCompletesNormally0(if_.getElsePart().getBody(), labelsToIgnore); + return if_.getElsePart() != null && + neverCompletesNormally0(if_.getThenPart(), labelsToIgnore) && + neverCompletesNormally0(if_.getElsePart().getBody(), labelsToIgnore); } else if (statement instanceof J.Switch) { J.Switch switch_ = (J.Switch) statement; if (switch_.getCases().getStatements().isEmpty()) { @@ -240,13 +240,13 @@ && neverCompletesNormally0(if_.getThenPart(), labelsToIgnore) return neverCompletesNormally0(getLastStatement(case_), labelsToIgnore); } else if (statement instanceof J.Try) { J.Try try_ = (J.Try) statement; - if (try_.getFinally() != null && !try_.getFinally().getStatements().isEmpty() - && neverCompletesNormally0(try_.getFinally(), labelsToIgnore)) { + if (try_.getFinally() != null && !try_.getFinally().getStatements().isEmpty() && + neverCompletesNormally0(try_.getFinally(), labelsToIgnore)) { return true; } boolean bodyHasExit = false; - if (!try_.getBody().getStatements().isEmpty() - && !(bodyHasExit = neverCompletesNormally0(try_.getBody(), labelsToIgnore))) { + if (!try_.getBody().getStatements().isEmpty() && + !(bodyHasExit = neverCompletesNormally0(try_.getBody(), labelsToIgnore))) { return false; } for (J.Try.Catch catch_ : try_.getCatches()) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/internal/template/Substitutions.java b/rewrite-java/src/main/java/org/openrewrite/java/internal/template/Substitutions.java index 278e639c6a8..bb6c4cf4d3c 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/internal/template/Substitutions.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/internal/template/Substitutions.java @@ -133,8 +133,8 @@ private String substituteTypedPattern(String key, int index, TemplateParameterPa if (param != null) { type = TypeParameter.toFullyQualifiedName(param); } else { - if (parameter instanceof J.NewClass && ((J.NewClass) parameter).getBody() != null - && ((J.NewClass) parameter).getClazz() != null) { + if (parameter instanceof J.NewClass && ((J.NewClass) parameter).getBody() != null && + ((J.NewClass) parameter).getClazz() != null) { // for anonymous classes get the type from the supertype type = ((J.NewClass) parameter).getClazz().getType(); } else if (parameter instanceof TypedTree) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/recipes/SourceSpecTextBlockIndentation.java b/rewrite-java/src/main/java/org/openrewrite/java/recipes/SourceSpecTextBlockIndentation.java index e9da6507a30..bc943d87670 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/recipes/SourceSpecTextBlockIndentation.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/recipes/SourceSpecTextBlockIndentation.java @@ -100,8 +100,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu } J.Literal withFixedSource = source.withValueSource(fixedSource.toString()); - if (withFixedSource.getPrefix().getComments().isEmpty() - && withFixedSource.getPrefix().getWhitespace().isEmpty()) { + if (withFixedSource.getPrefix().getComments().isEmpty() && + withFixedSource.getPrefix().getWhitespace().isEmpty()) { return maybeAutoFormat(withFixedSource, withFixedSource.withPrefix(Space.format("\n")), ctx); } return withFixedSource; diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/FindEmptyMethods.java b/rewrite-java/src/main/java/org/openrewrite/java/search/FindEmptyMethods.java index 46e4065a08d..02d2bccacb1 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/FindEmptyMethods.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/FindEmptyMethods.java @@ -95,9 +95,9 @@ private boolean isEmptyMethod(J.MethodDeclaration method) { private boolean isInterfaceMethod(J.MethodDeclaration method) { //noinspection ConstantConditions - return method.getMethodType().getDeclaringType() != null - && method.getMethodType().getDeclaringType().getKind() == JavaType.FullyQualified.Kind.Interface - && !method.hasModifier(J.Modifier.Type.Default); + return method.getMethodType().getDeclaringType() != null && + method.getMethodType().getDeclaringType().getKind() == JavaType.FullyQualified.Kind.Interface && + !method.hasModifier(J.Modifier.Type.Default); } private boolean hasSinglePublicNoArgsConstructor(List classStatements) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/FindMethods.java b/rewrite-java/src/main/java/org/openrewrite/java/search/FindMethods.java index 5eb4d09bd18..f5815c5ed82 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/FindMethods.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/FindMethods.java @@ -75,8 +75,8 @@ public TreeVisitor getVisitor() { public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { // In an annotation @Example(value = "") the identifier "value" may have a method type J.Identifier i = super.visitIdentifier(identifier, ctx); - if(i.getType() instanceof JavaType.Method && methodMatcher.matches((JavaType.Method) i.getType()) - && !(getCursor().getParentTreeCursor().getValue() instanceof J.MethodInvocation)) { + if(i.getType() instanceof JavaType.Method && methodMatcher.matches((JavaType.Method) i.getType()) && + !(getCursor().getParentTreeCursor().getValue() instanceof J.MethodInvocation)) { JavaType.Method m = (JavaType.Method) i.getType(); JavaSourceFile javaSourceFile = getCursor().firstEnclosing(JavaSourceFile.class); if(javaSourceFile != null) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/FindMissingTypes.java b/rewrite-java/src/main/java/org/openrewrite/java/search/FindMissingTypes.java index 10fe01d333c..036e98ef66a 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/FindMissingTypes.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/FindMissingTypes.java @@ -231,10 +231,10 @@ public J.ParameterizedType visitParameterizedType(J.ParameterizedType type, Exec } private boolean isAllowedToHaveNullType(J.Identifier ident) { - return inPackageDeclaration() || inImport() || isClassName() - || isMethodName() || isMethodInvocationName() || isFieldAccess(ident) || isBeingDeclared(ident) || isParameterizedType(ident) - || isNewClass(ident) || isTypeParameter() || isMemberReference(ident) || isCaseLabel() || isLabel() || isAnnotationField(ident) - || isInJavaDoc(ident); + return inPackageDeclaration() || inImport() || isClassName() || + isMethodName() || isMethodInvocationName() || isFieldAccess(ident) || isBeingDeclared(ident) || isParameterizedType(ident) || + isNewClass(ident) || isTypeParameter() || isMemberReference(ident) || isCaseLabel() || isLabel() || isAnnotationField(ident) || + isInJavaDoc(ident); } private boolean inPackageDeclaration() { @@ -262,8 +262,8 @@ private boolean isMethodInvocationName() { private boolean isFieldAccess(J.Identifier ident) { Tree value = getCursor().getParentTreeCursor().getValue(); - return value instanceof J.FieldAccess - && (ident == ((J.FieldAccess) value).getName() || + return value instanceof J.FieldAccess && + (ident == ((J.FieldAccess) value).getName() || ident == ((J.FieldAccess) value).getTarget() && !((J.FieldAccess) value).getSimpleName().equals("class")); } @@ -283,8 +283,8 @@ private boolean isNewClass(J.Identifier ident) { } private boolean isTypeParameter() { - return getCursor().getParent() != null - && getCursor().getParent().getValue() instanceof J.TypeParameter; + return getCursor().getParent() != null && + getCursor().getParent().getValue() instanceof J.TypeParameter; } private boolean isMemberReference(J.Identifier ident) { @@ -309,8 +309,8 @@ private boolean isLabel() { private boolean isAnnotationField(J.Identifier ident) { Cursor parent = getCursor().getParent(); - return parent != null && parent.getValue() instanceof J.Assignment - && (ident == ((J.Assignment) parent.getValue()).getVariable() && getCursor().firstEnclosing(J.Annotation.class) != null); + return parent != null && parent.getValue() instanceof J.Assignment && + (ident == ((J.Assignment) parent.getValue()).getVariable() && getCursor().firstEnclosing(J.Annotation.class) != null); } } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java b/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java index 7513f29024d..b6bbcdce598 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/SemanticallyEqual.java @@ -616,8 +616,8 @@ public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, J j) { isEqual.set(false); return fieldAccess; } - } else if (!TypeUtils.isOfType(fieldAccess.getType(), compareTo.getType()) - || !TypeUtils.isOfType(fieldType, compareTo.getName().getFieldType())) { + } else if (!TypeUtils.isOfType(fieldAccess.getType(), compareTo.getType()) || + !TypeUtils.isOfType(fieldType, compareTo.getName().getFieldType())) { isEqual.set(false); return fieldAccess; } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java index 6b5237e6697..34eac9f1058 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java @@ -114,8 +114,8 @@ private JavaSourceFile maybeMark(JavaSourceFile c, @Nullable JavaType type) { return c; } - if (typePattern != null && TypeUtils.isAssignableTo(typePattern, type) - || fullyQualifiedType != null && TypeUtils.isAssignableTo(fullyQualifiedType, type)) { + if (typePattern != null && TypeUtils.isAssignableTo(typePattern, type) || + fullyQualifiedType != null && TypeUtils.isAssignableTo(fullyQualifiedType, type)) { return SearchResult.found(c); } diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java b/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java index c0cf10fe56e..72573ead397 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/Autodetect.java @@ -421,8 +421,8 @@ public Expression visitExpression(Expression expression, IndentStatistics stats) // (newline-separated) annotations on some common target are not continuations boolean isContinuation = !(expression instanceof J.Annotation && !( // ...but annotations which are *arguments* to other annotations can be continuations - getCursor().getParentTreeCursor().getValue() instanceof J.Annotation - || getCursor().getParentTreeCursor().getValue() instanceof J.NewArray + getCursor().getParentTreeCursor().getValue() instanceof J.Annotation || + getCursor().getParentTreeCursor().getValue() instanceof J.NewArray )); countIndents(expression.getPrefix().getWhitespace(), isContinuation, stats); diff --git a/rewrite-java/src/main/java/org/openrewrite/java/style/CheckstyleConfigLoader.java b/rewrite-java/src/main/java/org/openrewrite/java/style/CheckstyleConfigLoader.java index 1d0b058b778..33065e80fe4 100755 --- a/rewrite-java/src/main/java/org/openrewrite/java/style/CheckstyleConfigLoader.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/style/CheckstyleConfigLoader.java @@ -551,8 +551,8 @@ public String getName() { } public boolean prop(String key, boolean defaultValue) { - return properties.containsKey(key) ? parseBoolean(properties.get(key)) - : defaultValue; + return properties.containsKey(key) ? parseBoolean(properties.get(key)) : + defaultValue; } @Override diff --git a/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java b/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java index c8aa15fafa1..6b57a6bab86 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/tree/J.java @@ -5898,16 +5898,16 @@ public

J acceptJava(JavaVisitor

v, P p) { public Cursor getDeclaringScope(Cursor cursor) { return cursor.dropParentUntil(it -> - it instanceof J.Block - || it instanceof J.Lambda - || it instanceof J.MethodDeclaration - || it == Cursor.ROOT_VALUE); + it instanceof J.Block || + it instanceof J.Lambda || + it instanceof J.MethodDeclaration || + it == Cursor.ROOT_VALUE); } public boolean isField(Cursor cursor) { Cursor declaringScope = getDeclaringScope(cursor); - return declaringScope.getValue() instanceof J.Block - && declaringScope.getParentTreeCursor().getValue() instanceof J.ClassDeclaration; + return declaringScope.getValue() instanceof J.Block && + declaringScope.getParentTreeCursor().getValue() instanceof J.ClassDeclaration; } public Padding getPadding() { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java b/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java index 9ea02a41a1a..c4b432f87c5 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java @@ -326,8 +326,8 @@ public String getPackageName() { public boolean isAssignableTo(String fullyQualifiedName) { return TypeUtils.fullyQualifiedNamesAreEqual(getFullyQualifiedName(), fullyQualifiedName) || - getInterfaces().stream().anyMatch(anInterface -> anInterface.isAssignableTo(fullyQualifiedName)) - || (getSupertype() != null && getSupertype().isAssignableTo(fullyQualifiedName)); + getInterfaces().stream().anyMatch(anInterface -> anInterface.isAssignableTo(fullyQualifiedName)) || + (getSupertype() != null && getSupertype().isAssignableTo(fullyQualifiedName)); } public boolean isAssignableFrom(@Nullable JavaType type) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java b/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java index 117869d2f87..789c1bea8fe 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/tree/TypeUtils.java @@ -89,8 +89,8 @@ public static String toFullyQualifiedName(String fqn) { public static boolean fullyQualifiedNamesAreEqual(@Nullable String fqn1, @Nullable String fqn2) { if (fqn1 != null && fqn2 != null) { - return fqn1.equals(fqn2) || fqn1.length() == fqn2.length() - && toFullyQualifiedName(fqn1).equals(toFullyQualifiedName(fqn2)); + return fqn1.equals(fqn2) || fqn1.length() == fqn2.length() && + toFullyQualifiedName(fqn1).equals(toFullyQualifiedName(fqn2)); } return fqn1 == null && fqn2 == null; } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java index 3688a54111a..c8257bc8752 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddDependency.java @@ -218,8 +218,8 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) { // If the dependency is already in compile scope it will be available everywhere, no need to continue for (ResolvedDependency d : getResolutionResult().getDependencies().get(Scope.Compile)) { - if (hasAcceptableTransitivity(d, acc) - && groupId.equals(d.getGroupId()) && artifactId.equals(d.getArtifactId())) { + if (hasAcceptableTransitivity(d, acc) && + groupId.equals(d.getGroupId()) && artifactId.equals(d.getArtifactId())) { return maven; } } @@ -228,8 +228,8 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) { Scope resolvedScopeEnum = Scope.fromName(resolvedScope); if (resolvedScopeEnum == Scope.Provided || resolvedScopeEnum == Scope.Test) { for (ResolvedDependency d : getResolutionResult().getDependencies().get(resolvedScopeEnum)) { - if (hasAcceptableTransitivity(d, acc) - && groupId.equals(d.getGroupId()) && artifactId.equals(d.getArtifactId())) { + if (hasAcceptableTransitivity(d, acc) && + groupId.equals(d.getGroupId()) && artifactId.equals(d.getArtifactId())) { return maven; } } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java index f07b1f64752..10c49757fbb 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddManagedDependency.java @@ -238,9 +238,9 @@ public Xml visitDocument(Xml.Document document, ExecutionContext ctx) { .map(resolvedManagedDep -> { if (resolvedManagedDep.matches(groupId, artifactId, type, classifier)) { return resolvedManagedDep.getGav().getVersion(); - } else if (resolvedManagedDep.getRequestedBom() != null - && resolvedManagedDep.getRequestedBom().getGroupId().equals(groupId) - && resolvedManagedDep.getRequestedBom().getArtifactId().equals(artifactId)) { + } else if (resolvedManagedDep.getRequestedBom() != null && + resolvedManagedDep.getRequestedBom().getGroupId().equals(groupId) && + resolvedManagedDep.getRequestedBom().getArtifactId().equals(artifactId)) { return resolvedManagedDep.getRequestedBom().getVersion(); } return null; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddPluginDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddPluginDependency.java index d224d282d16..dc6b15b2f3a 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddPluginDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddPluginDependency.java @@ -104,15 +104,15 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) { dependencies = Xml.Tag.build("").withPrefix("\n"); plugins = addToTag(plugins, plugin, dependencies, getCursor().getParentOrThrow()); } - Xml.Tag newDependencyTag = Xml.Tag.build("\n" + groupId + "\n" - + artifactId + "" + ((version == null) ? "\n" : "\n" + version + "\n") + "") + Xml.Tag newDependencyTag = Xml.Tag.build("\n" + groupId + "\n" + + artifactId + "" + ((version == null) ? "\n" : "\n" + version + "\n") + "") .withPrefix("\n"); // The dependency being added may already exist and may or may not need its version updated Optional maybeExistingDependency = dependencies.getChildren() .stream() - .filter(it -> groupId.equals(it.getChildValue("groupId").orElse(null)) - && artifactId.equals(it.getChildValue("artifactId").orElse(null))) + .filter(it -> groupId.equals(it.getChildValue("groupId").orElse(null)) && + artifactId.equals(it.getChildValue("artifactId").orElse(null))) .findAny(); if (maybeExistingDependency.isPresent() && areEqual(newDependencyTag, maybeExistingDependency.get())) { return plugins; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddProperty.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddProperty.java index aa88e23ec83..fa4ce4e218d 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddProperty.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddProperty.java @@ -76,8 +76,8 @@ public TreeVisitor getVisitor() { @Override public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { String parentValue = getResolutionResult().getPom().getRequested().getProperties().get(key); - if ((Boolean.TRUE.equals(trustParent) && (parentValue == null || value.equals(parentValue))) - || value.equals(getResolutionResult().getPom().getProperties().get(key))) { + if ((Boolean.TRUE.equals(trustParent) && (parentValue == null || value.equals(parentValue))) || + value.equals(getResolutionResult().getPom().getProperties().get(key))) { return document; } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddPropertyVisitor.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddPropertyVisitor.java index c4c9c842d84..b444d5f1ef9 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddPropertyVisitor.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddPropertyVisitor.java @@ -54,9 +54,9 @@ public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { @Override public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { - if (!Boolean.TRUE.equals(preserveExistingValue) - && isPropertyTag() && key.equals(tag.getName()) - && !value.equals(tag.getValue().orElse(null))) { + if (!Boolean.TRUE.equals(preserveExistingValue) && + isPropertyTag() && key.equals(tag.getName()) && + !value.equals(tag.getValue().orElse(null))) { doAfterVisit(new ChangeTagValueVisitor<>(tag, value)); } return super.visitTag(tag, ctx); diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/AddRepository.java b/rewrite-maven/src/main/java/org/openrewrite/maven/AddRepository.java index 2c8011df374..e42ac01d111 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/AddRepository.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/AddRepository.java @@ -267,9 +267,9 @@ private boolean isReleasesEqual(Xml.Tag repo) { if (releases == null) { return isNoReleases(); } else { - return Objects.equals(releasesEnabled == null ? null : String.valueOf(releasesEnabled.booleanValue()), releases.getChildValue("enabled").orElse(null)) - && Objects.equals(releasesUpdatePolicy, releases.getChildValue("updatePolicy").orElse(null)) - && Objects.equals(releasesChecksumPolicy, releases.getChildValue("checksumPolicy").orElse(null)); + return Objects.equals(releasesEnabled == null ? null : String.valueOf(releasesEnabled.booleanValue()), releases.getChildValue("enabled").orElse(null)) && + Objects.equals(releasesUpdatePolicy, releases.getChildValue("updatePolicy").orElse(null)) && + Objects.equals(releasesChecksumPolicy, releases.getChildValue("checksumPolicy").orElse(null)); } } @@ -282,9 +282,9 @@ private boolean isSnapshotsEqual(Xml.Tag repo) { if (snapshots == null) { return isNoSnapshots(); } else { - return Objects.equals(snapshotsEnabled == null ? null : String.valueOf(snapshotsEnabled.booleanValue()), snapshots.getChildValue("enabled").orElse(null)) - && Objects.equals(snapshotsUpdatePolicy, snapshots.getChildValue("updatePolicy").orElse(null)) - && Objects.equals(snapshotsChecksumPolicy, snapshots.getChildValue("checksumPolicy").orElse(null)); + return Objects.equals(snapshotsEnabled == null ? null : String.valueOf(snapshotsEnabled.booleanValue()), snapshots.getChildValue("enabled").orElse(null)) && + Objects.equals(snapshotsUpdatePolicy, snapshots.getChildValue("updatePolicy").orElse(null)) && + Objects.equals(snapshotsChecksumPolicy, snapshots.getChildValue("checksumPolicy").orElse(null)); } } diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/ChangeParentPom.java b/rewrite-maven/src/main/java/org/openrewrite/maven/ChangeParentPom.java index d58d187a4dc..b5e517c0ced 100755 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/ChangeParentPom.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/ChangeParentPom.java @@ -309,8 +309,8 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { } private boolean isGlobalProperty(String propertyName) { - return propertyName.startsWith("project.") || propertyName.startsWith("env.") - || propertyName.startsWith("settings.") || propertyName.equals("basedir"); + return propertyName.startsWith("project.") || propertyName.startsWith("env.") || + propertyName.startsWith("settings.") || propertyName.equals("basedir"); } }.visit(pomXml, ctx); return properties; @@ -384,8 +384,8 @@ public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { @Override public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { Xml.Tag t = super.visitTag(tag, ctx); - if (isPropertyTag() && key.equals(tag.getName()) - && !value.equals(tag.getValue().orElse(null))) { + if (isPropertyTag() && key.equals(tag.getName()) && + !value.equals(tag.getValue().orElse(null))) { t = (Xml.Tag) new ChangeTagValueVisitor<>(tag, value).visitNonNull(t, ctx); } return t; diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/IncrementProjectVersion.java b/rewrite-maven/src/main/java/org/openrewrite/maven/IncrementProjectVersion.java index c5028ac981d..df6b9fa3c8d 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/IncrementProjectVersion.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/IncrementProjectVersion.java @@ -165,8 +165,8 @@ public TreeVisitor getVisitor(Map ac public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { Xml.Tag t = super.visitTag(tag, ctx); - if ((!(PROJECT_MATCHER.matches(getCursor()) || PARENT_MATCHER.matches(getCursor()))) - || t.getMarkers().findFirst(AlreadyIncremented.class).isPresent()) { + if ((!(PROJECT_MATCHER.matches(getCursor()) || PARENT_MATCHER.matches(getCursor()))) || + t.getMarkers().findFirst(AlreadyIncremented.class).isPresent()) { return t; } String newVersion = acc.get(new GroupArtifact( diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java index 559586b65b1..342e08ddb04 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/MavenVisitor.java @@ -201,8 +201,8 @@ public boolean isManagedDependencyImportTag(String groupId, String artifactId) { return false; } Xml.Tag tag = getCursor().getValue(); - return tag.getChildValue("type").map("pom"::equalsIgnoreCase).orElse(false) - && tag.getChildValue("scope").map("import"::equalsIgnoreCase).orElse(false); + return tag.getChildValue("type").map("pom"::equalsIgnoreCase).orElse(false) && + tag.getChildValue("scope").map("import"::equalsIgnoreCase).orElse(false); } public void maybeUpdateModel() { diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/RemovePluginDependency.java b/rewrite-maven/src/main/java/org/openrewrite/maven/RemovePluginDependency.java index 258314db4f0..b8d76551955 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/RemovePluginDependency.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/RemovePluginDependency.java @@ -100,8 +100,8 @@ public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) { } Xml.Tag dependencies = maybeDependencies.get(); plugins = filterTagChildren(plugins, dependencies, dependencyTag -> - !(childValueMatches(dependencyTag, "groupId", groupId) - && childValueMatches(dependencyTag, "artifactId", artifactId)) + !(childValueMatches(dependencyTag, "groupId", groupId) && + childValueMatches(dependencyTag, "artifactId", artifactId)) ); plugins = filterTagChildren(plugins, plugin, pluginChildTag -> !(pluginChildTag.getName().equals("dependencies") && pluginChildTag.getChildren().isEmpty())); diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java index 4f4f8df299e..f3f16dd54fe 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/RemoveRedundantDependencyVersions.java @@ -74,8 +74,8 @@ public class RemoveRedundantDependencyVersions extends Recipe { Comparator onlyIfManagedVersionIs; @Option(displayName = "Except", - description = "Accepts a list of GAVs. Dependencies matching a GAV will be ignored by this recipe." - + " GAV versions are ignored if provided.", + description = "Accepts a list of GAVs. Dependencies matching a GAV will be ignored by this recipe." + + " GAV versions are ignored if provided.", example = "com.jcraft:jsch", required = false) @Nullable diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/UpdateMavenWrapper.java b/rewrite-maven/src/main/java/org/openrewrite/maven/UpdateMavenWrapper.java index 759f79feef0..9a9d0dec924 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/UpdateMavenWrapper.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/UpdateMavenWrapper.java @@ -494,8 +494,8 @@ public Properties.Entry visitEntry(Properties.Entry entry, ExecutionContext ctx) return null; } } else if (WRAPPER_SHA_256_SUM_KEY.equals(entry.getKey())) { - if (mavenWrapper.getWrapperDistributionType() != DistributionType.OnlyScript - && Boolean.TRUE.equals(enforceWrapperChecksumVerification)) { + if (mavenWrapper.getWrapperDistributionType() != DistributionType.OnlyScript && + Boolean.TRUE.equals(enforceWrapperChecksumVerification)) { Properties.Value value = entry.getValue(); Checksum wrapperJarChecksum = mavenWrapper.getWrapperChecksum(); if (wrapperJarChecksum != null && !wrapperJarChecksum.getHexValue().equals(value.getText())) { diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java index 574a1422661..2fa42d3482a 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradeDependencyVersion.java @@ -91,9 +91,9 @@ public class UpgradeDependencyVersion extends ScanningRecipe incomingRequestedDepend for (Dependency incReqDep : incomingRequestedDependencies) { boolean found = false; for (Dependency reqDep : requestedDependencies) { - if (reqDep.getGav().getGroupId().equals(incReqDep.getGav().getGroupId()) - && reqDep.getArtifactId().equals(incReqDep.getArtifactId())) { + if (reqDep.getGav().getGroupId().equals(incReqDep.getGav().getGroupId()) && + reqDep.getArtifactId().equals(incReqDep.getArtifactId())) { found = true; break; } @@ -831,9 +831,9 @@ private boolean isAlreadyResolved(GroupArtifactVersion groupArtifactVersion, Lis for (int i = 1; i < pomAncestry.size(); i++) { // skip current pom Pom pom = pomAncestry.get(i); ResolvedGroupArtifactVersion alreadyResolvedGav = pom.getGav(); - if (alreadyResolvedGav.getGroupId().equals(groupArtifactVersion.getGroupId()) - && alreadyResolvedGav.getArtifactId().equals(groupArtifactVersion.getArtifactId()) - && alreadyResolvedGav.getVersion().equals(groupArtifactVersion.getVersion())) { + if (alreadyResolvedGav.getGroupId().equals(groupArtifactVersion.getGroupId()) && + alreadyResolvedGav.getArtifactId().equals(groupArtifactVersion.getArtifactId()) && + alreadyResolvedGav.getVersion().equals(groupArtifactVersion.getVersion())) { return true; } } diff --git a/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyKey.java b/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyKey.java index 729d5ddc982..24d9e7cdbf3 100755 --- a/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyKey.java +++ b/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyKey.java @@ -74,16 +74,16 @@ public ChangePropertyKeyVisitor() { @Override public Properties visitEntry(Properties.Entry entry, P p) { if (Boolean.TRUE.equals(regex)) { - if (!Boolean.FALSE.equals(relaxedBinding) - ? NameCaseConvention.matchesRegexRelaxedBinding(entry.getKey(), oldPropertyKey) - : entry.getKey().matches(oldPropertyKey)) { + if (!Boolean.FALSE.equals(relaxedBinding) ? + NameCaseConvention.matchesRegexRelaxedBinding(entry.getKey(), oldPropertyKey) : + entry.getKey().matches(oldPropertyKey)) { entry = entry.withKey(entry.getKey().replaceFirst(oldPropertyKey, newPropertyKey)) .withPrefix(entry.getPrefix()); } } else { - if (!Boolean.FALSE.equals(relaxedBinding) - ? NameCaseConvention.equalsRelaxedBinding(entry.getKey(), oldPropertyKey) - : entry.getKey().equals(oldPropertyKey)) { + if (!Boolean.FALSE.equals(relaxedBinding) ? + NameCaseConvention.equalsRelaxedBinding(entry.getKey(), oldPropertyKey) : + entry.getKey().equals(oldPropertyKey)) { entry = entry.withKey(newPropertyKey) .withPrefix(entry.getPrefix()); } diff --git a/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyValue.java b/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyValue.java index 68b5186c26b..4ddcb852df4 100755 --- a/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyValue.java +++ b/rewrite-properties/src/main/java/org/openrewrite/properties/ChangePropertyValue.java @@ -98,23 +98,23 @@ public Properties visitEntry(Properties.Entry entry, P p) { // returns null if value should not change private Properties.@Nullable Value updateValue(Properties.Value value) { - Properties.Value updatedValue = value.withText(Boolean.TRUE.equals(regex) - ? value.getText().replaceAll(oldValue, newValue) - : newValue); + Properties.Value updatedValue = value.withText(Boolean.TRUE.equals(regex) ? + value.getText().replaceAll(oldValue, newValue) : + newValue); return updatedValue.getText().equals(value.getText()) ? null : updatedValue; } private boolean matchesPropertyKey(String prop) { - return !Boolean.FALSE.equals(relaxedBinding) - ? NameCaseConvention.matchesGlobRelaxedBinding(prop, propertyKey) - : StringUtils.matchesGlob(prop, propertyKey); + return !Boolean.FALSE.equals(relaxedBinding) ? + NameCaseConvention.matchesGlobRelaxedBinding(prop, propertyKey) : + StringUtils.matchesGlob(prop, propertyKey); } private boolean matchesOldValue(Properties.Value value) { return StringUtils.isNullOrEmpty(oldValue) || - (Boolean.TRUE.equals(regex) - ? Pattern.compile(oldValue).matcher(value.getText()).find() - : value.getText().equals(oldValue)); + (Boolean.TRUE.equals(regex) ? + Pattern.compile(oldValue).matcher(value.getText()).find() : + value.getText().equals(oldValue)); } } diff --git a/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java b/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java index 42af9090ac9..a1259aecdac 100644 --- a/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java +++ b/rewrite-test/src/main/java/org/openrewrite/test/RewriteTest.java @@ -428,8 +428,8 @@ default void rewriteRun(Consumer spec, SourceSpec... sourceSpecs) return " " + beforePath + " -> " + afterPath; }) .collect(Collectors.joining("\n")); - fail("Expected a new source file with the source path: " + sourceSpec.getSourcePath() - +"\nAll source file paths, before and after recipe run:\n" + paths); + fail("Expected a new source file with the source path: " + sourceSpec.getSourcePath() + + "\nAll source file paths, before and after recipe run:\n" + paths); } // If the source spec has not defined a source path, look for a result with the exact contents. This logic @@ -579,10 +579,10 @@ default void rewriteRun(Consumer spec, SourceSpec... sourceSpecs) newFilesGenerated.assertAll(); Map resultToUnexpected = allResults.stream() - .collect(Collectors.toMap(result -> result, result -> result.getBefore() == null - && !(result.getAfter() instanceof Remote) - && !expectedNewResults.contains(result) - && testMethodSpec.afterRecipes.isEmpty())); + .collect(Collectors.toMap(result -> result, result -> result.getBefore() == null && + !(result.getAfter() instanceof Remote) && + !expectedNewResults.contains(result) && + testMethodSpec.afterRecipes.isEmpty())); if (resultToUnexpected.values().stream().anyMatch(unexpected -> unexpected)) { String paths = resultToUnexpected.entrySet().stream() .map(it -> { diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeNamespaceValue.java b/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeNamespaceValue.java index 7f7b4e49fbe..b9941bfb8ae 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeNamespaceValue.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeNamespaceValue.java @@ -225,8 +225,8 @@ private Optional maybeGetSchemaLocation(Namespaced n) { Map namespaces = n.getNamespaces(); for (Xml.Attribute attribute : n.getAttributes()) { String attributeNamespace = namespaces.get(Namespaced.extractNamespacePrefix(attribute.getKeyAsString())); - if(XML_SCHEMA_INSTANCE_URI.equals(attributeNamespace) - && attribute.getKeyAsString().endsWith("schemaLocation")) { + if(XML_SCHEMA_INSTANCE_URI.equals(attributeNamespace) && + attribute.getKeyAsString().endsWith("schemaLocation")) { return Optional.of(attribute); } } @@ -280,9 +280,9 @@ public Xml.Tag withNamespaces(Xml.Tag tag, Map namespaces) { if (attributeByKey.containsKey(key)) { Xml.Attribute attribute = attributeByKey.get(key); if (!ns.getValue().equals(attribute.getValueAsString())) { - ListUtils.map(attributes, a -> a.getKeyAsString().equals(key) - ? attribute.withValue(new Xml.Attribute.Value(randomId(), "", Markers.EMPTY, Xml.Attribute.Value.Quote.Double, ns.getValue())) - : a + ListUtils.map(attributes, a -> a.getKeyAsString().equals(key) ? + attribute.withValue(new Xml.Attribute.Value(randomId(), "", Markers.EMPTY, Xml.Attribute.Value.Quote.Double, ns.getValue())) : + a ); } } else { diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeTagAttribute.java b/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeTagAttribute.java index 1c8a6b93a39..deda5df7396 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeTagAttribute.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/ChangeTagAttribute.java @@ -102,9 +102,9 @@ public Xml.Attribute visitChosenElementAttribute(Xml.Attribute attribute) { return null; } - String changedValue = oldValue != null - ? (Boolean.TRUE.equals(regex) ? stringValue.replaceAll(oldValue, newValue) : stringValue.replace(oldValue, newValue)) - : newValue; + String changedValue = oldValue != null ? + (Boolean.TRUE.equals(regex) ? stringValue.replaceAll(oldValue, newValue) : stringValue.replace(oldValue, newValue)) : + newValue; return attribute.withValue( new Xml.Attribute.Value(attribute.getId(), diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/XPathMatcher.java b/rewrite-xml/src/main/java/org/openrewrite/xml/XPathMatcher.java index 8ca660a975d..b3c184b9038 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/XPathMatcher.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/XPathMatcher.java @@ -123,8 +123,8 @@ public boolean matches(Cursor cursor) { boolean matchedCondition = false; Matcher matcher; - if (tagForCondition != null && partWithCondition.endsWith("]") - && (matcher = ELEMENT_WITH_CONDITION_PATTERN.matcher(partWithCondition)).matches()) { + if (tagForCondition != null && partWithCondition.endsWith("]") && + (matcher = ELEMENT_WITH_CONDITION_PATTERN.matcher(partWithCondition)).matches()) { String optionalPartName = matchesElementWithConditionFunction(matcher, tagForCondition, cursor); if (optionalPartName == null) { return false; @@ -150,16 +150,16 @@ public boolean matches(Cursor cursor) { continue; } - boolean conditionNotFulfilled = tagForCondition == null - || (!part.equals(partName) && !tagForCondition.getName().equals(partName)); + boolean conditionNotFulfilled = tagForCondition == null || + (!part.equals(partName) && !tagForCondition.getName().equals(partName)); int idx = part.indexOf("["); if (idx > 0) { part = part.substring(0, idx); } - if (path.size() < i + 1 - || (!(path.get(pathIndex).getName().equals(part)) && !"*".equals(part)) - || conditionIsBefore && conditionNotFulfilled) { + if (path.size() < i + 1 || + (!(path.get(pathIndex).getName().equals(part)) && !"*".equals(part)) || + conditionIsBefore && conditionNotFulfilled) { return false; } } diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/Namespaced.java b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/Namespaced.java index 9595f6c0da5..f7b50498487 100644 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/trait/Namespaced.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/trait/Namespaced.java @@ -234,8 +234,8 @@ public Matcher xPath(@Nullable XPathMatcher xPath) { Namespaced namespaced = new Namespaced(cursor); if (uri != null || prefix != null) { Map namespaces = namespaced.getNamespaces(); - if ((uri != null && !namespaces.containsValue(uri)) - || (prefix != null && !namespaces.containsKey(prefix))) { + if ((uri != null && !namespaces.containsValue(uri)) || + (prefix != null && !namespaces.containsKey(prefix))) { return null; } } diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyKey.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyKey.java index cee27689614..93a1f40c14b 100755 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyKey.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyKey.java @@ -110,8 +110,8 @@ public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, P p) { .map(e2 -> e2.getKey().getValue()) .collect(Collectors.joining(".")); - if (newPropertyKey.startsWith(oldPropertyKey) - && (matches(prop, newPropertyKey) || matches(prop, newPropertyKey + ".*") || childMatchesNewPropertyKey(entry, prop))) { + if (newPropertyKey.startsWith(oldPropertyKey) && + (matches(prop, newPropertyKey) || matches(prop, newPropertyKey + ".*") || childMatchesNewPropertyKey(entry, prop))) { return e; } @@ -122,8 +122,8 @@ public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, P p) { Yaml.Mapping.Entry propertyEntry = propertyEntriesLeftToRight.next(); String value = propertyEntry.getKey().getValue() + "."; - if ((!propertyToTest.startsWith(value ) || (propertyToTest.startsWith(value) && !propertyEntriesLeftToRight.hasNext())) - && hasNonExcludedValues(propertyEntry)) { + if ((!propertyToTest.startsWith(value ) || (propertyToTest.startsWith(value) && !propertyEntriesLeftToRight.hasNext())) && + hasNonExcludedValues(propertyEntry)) { doAfterVisit(new InsertSubpropertyVisitor<>( propertyEntry, propertyToTest, @@ -193,8 +193,8 @@ private boolean hasExcludedValues(Yaml.Mapping.Entry propertyEntry) { private boolean anyMatch(Yaml.Mapping.Entry entry, List subKeys) { for (String subKey : subKeys) { - if (entry.getKey().getValue().equals(subKey) - || entry.getKey().getValue().startsWith(subKey + ".")) { + if (entry.getKey().getValue().equals(subKey) || + entry.getKey().getValue().startsWith(subKey + ".")) { return true; } } @@ -203,8 +203,8 @@ private boolean anyMatch(Yaml.Mapping.Entry entry, List subKeys) { private static boolean noneMatch(Yaml.Mapping.Entry entry, List subKeys) { for (String subKey : subKeys) { - if (entry.getKey().getValue().equals(subKey) - || entry.getKey().getValue().startsWith(subKey + ".")) { + if (entry.getKey().getValue().equals(subKey) || + entry.getKey().getValue().startsWith(subKey + ".")) { return false; } } diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyValue.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyValue.java index cf28ca0ed20..0450162b720 100644 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyValue.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/ChangePropertyValue.java @@ -113,16 +113,16 @@ public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, ExecutionC return null; } Yaml.Scalar scalar = (Yaml.Scalar) value; - Yaml.Scalar newScalar = scalar.withValue(Boolean.TRUE.equals(regex) - ? scalar.getValue().replaceAll(Objects.requireNonNull(oldValue), newValue) - : newValue); + Yaml.Scalar newScalar = scalar.withValue(Boolean.TRUE.equals(regex) ? + scalar.getValue().replaceAll(Objects.requireNonNull(oldValue), newValue) : + newValue); return scalar.getValue().equals(newScalar.getValue()) ? null : newScalar; } private boolean matchesPropertyKey(String prop) { - return !Boolean.FALSE.equals(relaxedBinding) - ? NameCaseConvention.matchesGlobRelaxedBinding(prop, propertyKey) - : StringUtils.matchesGlob(prop, propertyKey); + return !Boolean.FALSE.equals(relaxedBinding) ? + NameCaseConvention.matchesGlobRelaxedBinding(prop, propertyKey) : + StringUtils.matchesGlob(prop, propertyKey); } private boolean matchesOldValue(Yaml.Block value) { @@ -131,9 +131,9 @@ private boolean matchesOldValue(Yaml.Block value) { } Yaml.Scalar scalar = (Yaml.Scalar) value; return StringUtils.isNullOrEmpty(oldValue) || - (Boolean.TRUE.equals(regex) - ? Pattern.compile(oldValue).matcher(scalar.getValue()).find() - : scalar.getValue().equals(oldValue)); + (Boolean.TRUE.equals(regex) ? + Pattern.compile(oldValue).matcher(scalar.getValue()).find() : + scalar.getValue().equals(oldValue)); } private static String getProperty(Cursor cursor) { diff --git a/rewrite-yaml/src/main/java/org/openrewrite/yaml/DeleteProperty.java b/rewrite-yaml/src/main/java/org/openrewrite/yaml/DeleteProperty.java index de995db8fa9..c262102d3e8 100755 --- a/rewrite-yaml/src/main/java/org/openrewrite/yaml/DeleteProperty.java +++ b/rewrite-yaml/src/main/java/org/openrewrite/yaml/DeleteProperty.java @@ -42,8 +42,8 @@ public class DeleteProperty extends Recipe { @Deprecated @Option(displayName = "Coalesce", - description = "(Deprecated: in a future version, this recipe will always use the `false` behavior)" - + " Simplify nested map hierarchies into their simplest dot separated property form.", + description = "(Deprecated: in a future version, this recipe will always use the `false` behavior)" + + " Simplify nested map hierarchies into their simplest dot separated property form.", required = false) @Nullable Boolean coalesce; From 3b2029274c780b4ec50668d6a1b964643ea0bf81 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 23 Sep 2024 11:42:47 +0200 Subject: [PATCH 77/82] Use alternate metadata local file path (#4512) --- .../openrewrite/maven/internal/MavenPomDownloader.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java index 1128692801f..915cf797bf7 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java @@ -256,15 +256,14 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv boolean cacheEmptyResult = false; try { String scheme = URI.create(repo.getUri()).getScheme(); - String uri = repo.getUri() + (repo.getUri().endsWith("/") ? "" : "/") + + String baseUri = repo.getUri() + (repo.getUri().endsWith("/") ? "" : "/") + requireNonNull(gav.getGroupId()).replace('.', '/') + '/' + gav.getArtifactId() + '/' + - (gav.getVersion() == null ? "" : gav.getVersion() + '/') + - "maven-metadata.xml"; + (gav.getVersion() == null ? "" : gav.getVersion() + '/'); if ("file".equals(scheme)) { // A maven repository can be expressed as a URI with a file scheme - Path path = Paths.get(URI.create(uri)); + Path path = Paths.get(URI.create(baseUri + "maven-metadata-local.xml")); if (Files.exists(path)) { MavenMetadata parsed = MavenMetadata.parse(Files.readAllBytes(path)); if (parsed != null) { @@ -272,7 +271,7 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv } } } else { - byte[] responseBody = requestAsAuthenticatedOrAnonymous(repo, uri); + byte[] responseBody = requestAsAuthenticatedOrAnonymous(repo, baseUri + "maven-metadata.xml"); MavenMetadata parsed = MavenMetadata.parse(responseBody); if (parsed != null) { result = Optional.of(parsed); From bf6af04ddf24d0e152cf819b799e4258d2cf8f2a Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 23 Sep 2024 11:47:42 +0200 Subject: [PATCH 78/82] Parse MavenMetadata.Versioning.lastUpdated as ZonedDateTime (#4511) Fixes https://github.com/openrewrite/rewrite/issues/4325 --- .../maven/internal/MavenPomDownloader.java | 12 ++++++--- .../maven/internal/MavenXmlMapper.java | 2 ++ .../openrewrite/maven/tree/MavenMetadata.java | 26 ++++++++++--------- .../maven/internal/MavenMetadataTest.java | 4 +++ 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java index 915cf797bf7..647a14e3e94 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenPomDownloader.java @@ -42,6 +42,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; +import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; @@ -391,7 +392,7 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv versions.add(path.getFileName().toString()); } } - return new MavenMetadata.Versioning(versions, null, null); + return new MavenMetadata.Versioning(versions, null, null, null); } catch (IOException e) { throw new MavenDownloadingException("Unable to derive metadata from file repository. " + e.getMessage(), null, gav); } @@ -421,7 +422,7 @@ public MavenMetadata downloadMetadata(GroupArtifactVersion gav, @Nullable Resolv return null; } - return new MavenMetadata.Versioning(versions, null, null); + return new MavenMetadata.Versioning(versions, null, null, null); } String hrefToVersion(String href, String rootUri) { @@ -445,10 +446,15 @@ protected MavenMetadata mergeMetadata(MavenMetadata m1, MavenMetadata m2) { mergeVersions(m1.getVersioning().getVersions(), m2.getVersioning().getVersions()), Stream.concat(m1.getVersioning().getSnapshotVersions() == null ? Stream.empty() : m1.getVersioning().getSnapshotVersions().stream(), m2.getVersioning().getSnapshotVersions() == null ? Stream.empty() : m2.getVersioning().getSnapshotVersions().stream()).collect(toList()), - maxSnapshot(m1.getVersioning().getSnapshot(), m2.getVersioning().getSnapshot()) + maxSnapshot(m1.getVersioning().getSnapshot(), m2.getVersioning().getSnapshot()), + maxLastUpdated(m1.getVersioning().getLastUpdated(), m2.getVersioning().getLastUpdated()) )); } + private @Nullable ZonedDateTime maxLastUpdated(@Nullable ZonedDateTime left, @Nullable ZonedDateTime right) { + return left == null ? right : right == null ? left : left.compareTo(right) >= 0 ? left : right; + } + private List mergeVersions(List versions1, List versions2) { Set merged = new HashSet<>(versions1); merged.addAll(versions2); diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenXmlMapper.java b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenXmlMapper.java index ac54c3f3969..15fa541779b 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenXmlMapper.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/internal/MavenXmlMapper.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.dataformat.xml.XmlFactory; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; @@ -63,6 +64,7 @@ public class MavenXmlMapper { .withGetterVisibility(JsonAutoDetect.Visibility.NONE) .withSetterVisibility(JsonAutoDetect.Visibility.NONE) .withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY)) + .registerModule(new JavaTimeModule()) .registerModule(new StringTrimModule()); writeMapper = XmlMapper.builder(xmlFactory) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenMetadata.java b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenMetadata.java index 019c5034b34..2ece965a7f5 100644 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenMetadata.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/tree/MavenMetadata.java @@ -15,6 +15,7 @@ */ package org.openrewrite.maven.tree; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import lombok.AccessLevel; import lombok.Getter; @@ -22,12 +23,11 @@ import lombok.experimental.FieldDefaults; import org.jspecify.annotations.Nullable; import org.openrewrite.maven.internal.MavenXmlMapper; -import org.openrewrite.xml.XmlParser; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.nio.file.Path; +import java.time.ZonedDateTime; import java.util.List; import static java.util.Collections.emptyList; @@ -35,14 +35,7 @@ @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter public class MavenMetadata { - private static final XmlParser xmlParser = new XmlParser() { - @Override - public boolean accept(Path path) { - return super.accept(path) || path.toString().endsWith(".pom"); - } - }; - - public static final MavenMetadata EMPTY = new MavenMetadata(new MavenMetadata.Versioning(emptyList(), emptyList(), null)); + public static final MavenMetadata EMPTY = new MavenMetadata(new MavenMetadata.Versioning(emptyList(), emptyList(), null, null)); Versioning versioning; @@ -62,10 +55,15 @@ public static class Versioning { @Nullable Snapshot snapshot; + @Nullable + ZonedDateTime lastUpdated; + public Versioning( @JacksonXmlElementWrapper(localName = "versions") List versions, @JacksonXmlElementWrapper(localName = "snapshotVersions") @Nullable List snapshotVersions, - @Nullable Snapshot snapshot) { + @Nullable Snapshot snapshot, + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyyMMddHHmmss", timezone = "UTC") @Nullable ZonedDateTime lastUpdated) { + this.lastUpdated = lastUpdated; this.versions = versions; this.snapshotVersions = snapshotVersions; this.snapshot = snapshot; @@ -83,7 +81,11 @@ public static MavenMetadata parse(InputStream document) { public static @Nullable MavenMetadata parse(byte[] document) throws IOException { MavenMetadata metadata = MavenXmlMapper.readMapper().readValue(document, MavenMetadata.class); if (metadata != null && metadata.getVersioning() != null && metadata.getVersioning().getVersions() == null) { - return new MavenMetadata(new Versioning(emptyList(), metadata.getVersioning().getSnapshotVersions(), metadata.getVersioning().getSnapshot())); + return new MavenMetadata(new Versioning( + emptyList(), + metadata.getVersioning().getSnapshotVersions(), + metadata.getVersioning().getSnapshot(), + metadata.getVersioning().getLastUpdated())); } return metadata; } diff --git a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenMetadataTest.java b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenMetadataTest.java index cd3b87a8195..5d38c46284c 100644 --- a/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenMetadataTest.java +++ b/rewrite-maven/src/test/java/org/openrewrite/maven/internal/MavenMetadataTest.java @@ -21,7 +21,9 @@ import org.openrewrite.maven.tree.MavenMetadata; import java.io.IOException; +import java.time.ZonedDateTime; +import static java.time.ZoneOffset.UTC; import static org.assertj.core.api.Assertions.assertThat; class MavenMetadataTest { @@ -46,6 +48,7 @@ void deserializeMetadata() throws IOException { MavenMetadata parsed = MavenMetadata.parse(metadata.getBytes()); assertThat(parsed.getVersioning().getVersions()).hasSize(2); + assertThat(parsed.getVersioning().getLastUpdated()).isEqualTo(ZonedDateTime.of(2021, 1, 15, 4, 27, 54, 0, UTC)); } @SuppressWarnings("ConstantConditions") @@ -80,6 +83,7 @@ void deserializeSnapshotMetadata() throws IOException { MavenMetadata parsed = MavenMetadata.parse(metadata.getBytes()); MavenMetadata.Versioning versioning = parsed.getVersioning(); + assertThat(versioning.getLastUpdated()).isNull(); assertThat(versioning.getSnapshot().getTimestamp()).isEqualTo("20220927.033510"); assertThat(versioning.getSnapshot().getBuildNumber()).isEqualTo("223"); assertThat(versioning.getVersions()).isNotNull(); From 4881ffd4e2b5dfaab21ce4a0644a10e78bd7a9ac Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 23 Sep 2024 13:56:35 +0200 Subject: [PATCH 79/82] Accommodate Java 23 in ReloadableJava21ParserVisitor (#4516) --- .../java/isolated/ReloadableJava21ParserVisitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java index 5079764cb0b..2c59e8f8924 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java @@ -16,6 +16,7 @@ package org.openrewrite.java.isolated; +import com.sun.source.doctree.DocCommentTree; import com.sun.source.tree.*; import com.sun.source.util.TreePathScanner; import com.sun.tools.javac.code.Flags; @@ -2136,7 +2137,7 @@ private List collectAnnotations(Map annotat return annotations; } - Space formatWithCommentTree(String prefix, JCTree tree, DCTree.@Nullable DCDocComment commentTree) { + Space formatWithCommentTree(String prefix, JCTree tree, @Nullable DocCommentTree commentTree) { Space fmt = format(prefix); if (commentTree != null) { List comments = fmt.getComments(); From 1a6a5621b464f0531d274651508f3d82c0423506 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Mon, 23 Sep 2024 23:25:21 -0500 Subject: [PATCH 80/82] refactor: Update Gradle wrapper (#4519) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sibmFtZSI6ImRpc3RyaWJ1dGlvbiIsInZhbHVlIjoiYmluIn1d Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 09e0523f895..3871bcf3b66 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1 +distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 From 2f24d5020ea9b5f41fef6741cfe286b52c7feda8 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 24 Sep 2024 09:23:37 +0200 Subject: [PATCH 81/82] Only bump Maven plugin version property when needed (#4517) --- .../java/org/openrewrite/maven/UpgradePluginVersion.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradePluginVersion.java b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradePluginVersion.java index ed49acf1cb2..3da649d8937 100755 --- a/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradePluginVersion.java +++ b/rewrite-maven/src/main/java/org/openrewrite/maven/UpgradePluginVersion.java @@ -182,8 +182,10 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) { if (versionTag.isPresent()) { String version = versionTag.get().getValue().orElse(null); if (version != null) { - if (version.trim().startsWith("${") && !newVersion.equals(getResolutionResult().getPom().getValue(version.trim()))) { - doAfterVisit(new ChangePropertyValue(version, newVersion, false, false).getVisitor()); + if (version.trim().startsWith("${")) { + if (!newVersion.equals(getResolutionResult().getPom().getValue(version.trim()))) { + doAfterVisit(new ChangePropertyValue(version, newVersion, false, false).getVisitor()); + } } else if (!newVersion.equals(version)) { doAfterVisit(new ChangeTagValueVisitor<>(versionTag.get(), newVersion)); } From d2c825c845bf9bf3259d633956fb0688b342d36b Mon Sep 17 00:00:00 2001 From: Bryce Tompkins <167870666+bryceatmoderne@users.noreply.github.com> Date: Tue, 24 Sep 2024 08:11:07 -0400 Subject: [PATCH 82/82] Parse .NET .vbproj and .fsproj files as XML (#4518) * Parse .vbproj and .fsproj as XML files * Test two more variants to guard against regression --------- Co-authored-by: Tim te Beek --- .../java/org/openrewrite/xml/XmlParser.java | 69 +++++++++++-------- .../org/openrewrite/xml/XmlParserTest.java | 33 ++++++++- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java index cca7af905b5..f99abd38118 100755 --- a/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java +++ b/rewrite-xml/src/main/java/org/openrewrite/xml/XmlParser.java @@ -32,9 +32,43 @@ import org.openrewrite.xml.tree.Xml; import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Stream; public class XmlParser implements Parser { + private static final Set ACCEPTED_FILE_EXTENSIONS = new HashSet<>(Arrays.asList( + "xml", + "wsdl", + "xhtml", + "xsd", + "xsl", + "xslt", + "xmi", + "tld", + "xjb", + "jsp", + // Datastage file formats that are all xml under the hood + "det", + "pjb", + "qjb", + "sjb", + "prt", + "srt", + "psc", + "ssc", + "tbd", + "tfm", + "dqs", + "stp", + "dcn", + "pst", + // .NET project files + "csproj", + "vbproj", + "fsproj")); + @Override public Stream parseInputs(Iterable sourceFiles, @Nullable Path relativeTo, ExecutionContext ctx) { ParsingEventListener parsingListener = ParsingExecutionContextView.view(ctx).getParsingListener(); @@ -76,34 +110,13 @@ public Stream parse(@Language("xml") String... sources) { @Override public boolean accept(Path path) { String p = path.toString(); - return p.endsWith(".xml") || - p.endsWith(".wsdl") || - p.endsWith(".xhtml") || - p.endsWith(".xsd") || - p.endsWith(".xsl") || - p.endsWith(".xslt") || - p.endsWith(".xmi") || - p.endsWith(".tld") || - p.endsWith(".xjb") || - p.endsWith(".jsp") || - // Datastage file formats that are all xml under the hood - p.endsWith(".det") || - p.endsWith(".pjb") || - p.endsWith(".qjb") || - p.endsWith(".sjb") || - p.endsWith(".prt") || - p.endsWith(".srt") || - p.endsWith(".psc") || - p.endsWith(".ssc") || - p.endsWith(".tbd") || - p.endsWith(".tfm") || - p.endsWith(".dqs") || - p.endsWith(".stp") || - p.endsWith(".dcn") || - p.endsWith(".pst") || - // C# project files - p.endsWith(".csproj") || - path.endsWith("packages.config"); + int dot = p.lastIndexOf('.'); + if (0 < dot && dot < (p.length() - 1)) { + if (ACCEPTED_FILE_EXTENSIONS.contains(p.substring(dot + 1))) { + return true; + } + } + return path.endsWith("packages.config"); } @Override diff --git a/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java b/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java index da793ec5f16..f8e5a000bb9 100755 --- a/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java +++ b/rewrite-xml/src/test/java/org/openrewrite/xml/XmlParserTest.java @@ -16,12 +16,19 @@ package org.openrewrite.xml; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.openrewrite.ExecutionContext; import org.openrewrite.Issue; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import org.openrewrite.xml.tree.Xml; +import java.nio.file.Paths; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.openrewrite.test.RewriteTest.toRecipe; import static org.openrewrite.xml.Assertions.xml; @@ -229,7 +236,7 @@ void parseDocTypeWithoutExternalId() { """ - + WARN @@ -372,4 +379,28 @@ void preserveWhitespaceOnEntities() { ) ); } + + @DisabledOnOs(OS.WINDOWS) + @ParameterizedTest + @ValueSource(strings = { + "foo.xml", + "proj.csproj", + "/foo/bar/baz.jsp", + "packages.config" + }) + void acceptWithValidPaths(String path) { + assertThat(new XmlParser().accept(Paths.get(path))).isTrue(); + } + + @DisabledOnOs(OS.WINDOWS) + @ParameterizedTest + @ValueSource(strings = { + ".xml", + "foo.xml.", + "file.cpp", + "/foo/bar/baz.xml.txt" + }) + void acceptWithInvalidPaths(String path) { + assertThat(new XmlParser().accept(Paths.get(path))).isFalse(); + } }