2828import org .openrewrite .xml .RemoveContentVisitor ;
2929import org .openrewrite .xml .tree .Xml ;
3030
31+ import java .nio .file .Path ;
3132import java .util .*;
3233
3334import static java .util .Collections .max ;
3738
3839@ Value
3940@ EqualsAndHashCode (callSuper = false )
40- public class ChangeDependencyGroupIdAndArtifactId extends Recipe {
41+ public class ChangeDependencyGroupIdAndArtifactId extends ScanningRecipe < ChangeDependencyGroupIdAndArtifactId . Accumulator > {
4142 transient MavenMetadataFailures metadataFailures = new MavenMetadataFailures (this );
4243
4344 @ Option (displayName = "Old groupId" ,
@@ -143,25 +144,19 @@ public Validated<Object> validate() {
143144 }
144145
145146 @ Override
146- public TreeVisitor <?, ExecutionContext > getVisitor () {
147+ public Accumulator getInitialValue (ExecutionContext ctx ) {
148+ return new Accumulator ();
149+ }
150+
151+ @ Override
152+ public TreeVisitor <?, ExecutionContext > getScanner (Accumulator acc ) {
147153 return new MavenVisitor <ExecutionContext >() {
148- @ Nullable
149- final VersionComparator versionComparator = newVersion != null ? Semver .validate (newVersion , versionPattern ).getValue () : null ;
150- @ Nullable
151- private Collection <String > availableVersions ;
154+ final @ Nullable VersionComparator versionComparator = newVersion != null ? Semver .validate (newVersion , versionPattern ).getValue () : null ;
152155 private boolean isNewDependencyPresent ;
153156
154157 @ Override
155158 public Xml visitDocument (Xml .Document document , ExecutionContext ctx ) {
156159 isNewDependencyPresent = checkIfNewDependencyPresents (newGroupId , newArtifactId , newVersion );
157- // Any managed dependency change is unlikely to use the same version, so only update selectively.
158- if ((changeManagedDependency == null || changeManagedDependency ) && newVersion != null || versionPattern != null ) {
159- doAfterVisit (new ChangeManagedDependencyGroupIdAndArtifactId (
160- oldGroupId , oldArtifactId ,
161- Optional .ofNullable (newGroupId ).orElse (oldGroupId ),
162- Optional .ofNullable (newArtifactId ).orElse (oldArtifactId ),
163- newVersion , versionPattern ).getVisitor ());
164- }
165160 return super .visitDocument (document , ctx );
166161 }
167162
@@ -170,58 +165,59 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
170165 Xml .Tag t = (Xml .Tag ) super .visitTag (tag , ctx );
171166 boolean isOldDependencyTag = isDependencyTag (oldGroupId , oldArtifactId );
172167 if (isOldDependencyTag && isNewDependencyPresent ) {
173- doAfterVisit (new RemoveContentVisitor <>(tag , true , true ));
174- maybeUpdateModel ();
168+ acc .isNewDependencyPresent = true ;
175169 return t ;
176170 }
177171 if (isOldDependencyTag || isPluginDependencyTag (oldGroupId , oldArtifactId )) {
178- String groupId = newGroupId ;
179- if (groupId != null ) {
180- t = changeChildTagValue (t , "groupId" , groupId , ctx );
181- } else {
182- groupId = t .getChildValue ("groupId" ).orElseThrow (NoSuchElementException ::new );
172+ Optional <String > groupIdFromTag = t .getChildValue ("groupId" );
173+ Optional <String > artifactIdFromTag = t .getChildValue ("artifactId" );
174+ if (!groupIdFromTag .isPresent () || !artifactIdFromTag .isPresent ()) {
175+ return t ;
183176 }
184- String artifactId = newArtifactId ;
185- if (artifactId != null ) {
186- t = changeChildTagValue (t , "artifactId" , artifactId , ctx );
187- } else {
188- artifactId = t .getChildValue ("artifactId" ).orElseThrow (NoSuchElementException ::new );
177+
178+ String groupId = groupIdFromTag .get ();
179+ if (newGroupId != null ) {
180+ storeParentPomProperty (groupId , newGroupId );
181+ groupId = newGroupId ;
182+ acc .changeGroupId = groupId ;
183+ }
184+
185+ String artifactId = artifactIdFromTag .get ();
186+ if (newArtifactId != null ) {
187+ storeParentPomProperty (artifactId , newArtifactId );
188+ artifactId = newArtifactId ;
189+ acc .changeArtifactId = artifactId ;
189190 }
190- String currentVersion = t . getChildValue ( "version" ). orElse ( null );
191+
191192 if (newVersion != null ) {
192193 try {
193- String resolvedNewVersion = resolveSemverVersion ( ctx , groupId , artifactId , currentVersion );
194- Optional < Xml . Tag > scopeTag = t . getChild ( "scope" );
195- Scope scope = scopeTag . map (xml -> Scope .fromName (xml .getValue ().orElse ("compile" ))).orElse (Scope .Compile );
194+ Optional < String > currentVersion = t . getChildValue ( "version" );
195+ String resolvedNewVersion = resolveSemverVersion ( groupId , artifactId , currentVersion . orElse ( newVersion ), ctx );
196+ Scope scope = t . getChild ( "scope" ). map (xml -> Scope .fromName (xml .getValue ().orElse (null ))).orElse (Scope .Compile );
196197 Optional <Xml .Tag > versionTag = t .getChild ("version" );
197198
198199 boolean configuredToOverrideManageVersion = overrideManagedVersion != null && overrideManagedVersion ; // False by default
199200 boolean configuredToChangeManagedDependency = changeManagedDependency == null || changeManagedDependency ; // True by default
200201
201- boolean versionTagPresent = versionTag .isPresent ();
202202 boolean oldDependencyManaged = isDependencyManaged (scope , oldGroupId , oldArtifactId );
203203 boolean newDependencyManaged = isDependencyManaged (scope , groupId , artifactId );
204- if (versionTagPresent ) {
204+ if (versionTag . isPresent () ) {
205205 // If the previous dependency had a version but the new artifact is managed, removed the version tag.
206206 if (!configuredToOverrideManageVersion && newDependencyManaged || (oldDependencyManaged && configuredToChangeManagedDependency )) {
207- t = ( Xml . Tag ) new RemoveContentVisitor <>( versionTag . get (), false , true ). visit ( t , ctx ) ;
207+ acc . removeVersionTag = true ;
208208 } else {
209209 // Otherwise, change the version to the new value.
210- t = changeChildTagValue (t , "version" , resolvedNewVersion , ctx );
210+ storeParentPomProperty (currentVersion .orElse (null ), resolvedNewVersion );
211+ acc .changeVersion = resolvedNewVersion ;
211212 }
212213 } else if (configuredToOverrideManageVersion || !newDependencyManaged ) {
213- //If the version is not present, add the version if we are explicitly overriding a managed version or if no managed version exists.
214- Xml .Tag newVersionTag = Xml .Tag .build ("<version>" + resolvedNewVersion + "</version>" );
215- //noinspection ConstantConditions
216- t = (Xml .Tag ) new AddToTagVisitor <ExecutionContext >(t , newVersionTag , new MavenTagInsertionComparator (t .getChildren ())).visitNonNull (t , ctx , getCursor ().getParent ());
214+ // If the version is not present, add the version if we are explicitly overriding a managed version or if no managed version exists.
215+ acc .createVersion = resolvedNewVersion ;
217216 }
218217 } catch (MavenDownloadingException e ) {
219218 return e .warn (tag );
220219 }
221220 }
222- if (t != tag ) {
223- maybeUpdateModel ();
224- }
225221 }
226222
227223 //noinspection ConstantConditions
@@ -249,23 +245,122 @@ private boolean isDependencyManaged(Scope scope, String groupId, String artifact
249245 }
250246
251247 @ SuppressWarnings ("ConstantConditions" )
252- private String resolveSemverVersion (ExecutionContext ctx , String groupId , String artifactId , @ Nullable String currentVersion ) throws MavenDownloadingException {
253- if (versionComparator == null ) {
254- return newVersion ;
248+ private String resolveSemverVersion (String groupId , String artifactId , String version , ExecutionContext ctx ) throws MavenDownloadingException {
249+ List <String > availableVersions = new ArrayList <>();
250+ MavenMetadata mavenMetadata = metadataFailures .insertRows (ctx , () -> downloadMetadata (groupId , artifactId , ctx ));
251+ for (String v : mavenMetadata .getVersioning ().getVersions ()) {
252+ if (versionComparator .isValid (version , v )) {
253+ availableVersions .add (v );
254+ }
255255 }
256- String finalCurrentVersion = currentVersion != null ? currentVersion : newVersion ;
257- if (availableVersions == null ) {
258- availableVersions = new ArrayList <>();
259- MavenMetadata mavenMetadata = metadataFailures .insertRows (ctx , () -> downloadMetadata (groupId , artifactId , ctx ));
260- for (String v : mavenMetadata .getVersioning ().getVersions ()) {
261- if (versionComparator .isValid (finalCurrentVersion , v )) {
262- availableVersions .add (v );
256+ return availableVersions .isEmpty () ? newVersion : max (availableVersions , versionComparator );
257+ }
258+
259+ private void storeParentPomProperty (@ Nullable String currentValue , String newValue ) {
260+ if (isProperty (currentValue )) {
261+ String name = currentValue .substring (2 , currentValue .length () - 1 ).trim ();
262+ if (!getResolutionResult ().getPom ().getRequested ().getProperties ().containsKey (name )) {
263+ storeParentPomProperty (getResolutionResult (), name , newValue );
264+ }
265+ }
266+ }
267+
268+ private void storeParentPomProperty (MavenResolutionResult resolutionResult , String name , String value ) {
269+ Pom pom = resolutionResult .getPom ().getRequested ();
270+ if (pom .getSourcePath () != null && pom .getProperties ().containsKey (name )) {
271+ acc .pomProperties .add (new PomProperty (pom .getSourcePath (), name , value ));
272+ } else if (resolutionResult .getParent () != null ) {
273+ storeParentPomProperty (resolutionResult .getParent (), name , value );
274+ }
275+ }
276+ };
277+ }
278+
279+ @ Override
280+ public TreeVisitor <?, ExecutionContext > getVisitor (Accumulator acc ) {
281+ return new MavenVisitor <ExecutionContext >() {
282+ @ Override
283+ public Xml visitDocument (Xml .Document document , ExecutionContext ctx ) {
284+ // Any managed dependency change is unlikely to use the same version, so only update selectively.
285+ if ((changeManagedDependency == null || changeManagedDependency ) && newVersion != null || versionPattern != null ) {
286+ doAfterVisit (new ChangeManagedDependencyGroupIdAndArtifactId (
287+ oldGroupId , oldArtifactId ,
288+ Optional .ofNullable (newGroupId ).orElse (oldGroupId ),
289+ Optional .ofNullable (newArtifactId ).orElse (oldArtifactId ),
290+ newVersion , versionPattern ).getVisitor ());
291+ }
292+ return super .visitDocument (document , ctx );
293+ }
294+
295+ @ Override
296+ public Xml visitTag (Xml .Tag tag , ExecutionContext ctx ) {
297+ Xml .Tag t = (Xml .Tag ) super .visitTag (tag , ctx );
298+ if (isPropertyTag ()) {
299+ Path sourcePath = getResolutionResult ().getPom ().getRequested ().getSourcePath ();
300+ for (PomProperty prop : acc .pomProperties ) {
301+ if (prop .filePath .equals (sourcePath )) {
302+ doAfterVisit (new ChangePropertyValue (prop .name , prop .value , false , false ).getVisitor ());
263303 }
264304 }
305+ return t ;
306+ }
265307
308+ boolean isOldDependencyTag = isDependencyTag (oldGroupId , oldArtifactId );
309+ if (isOldDependencyTag && acc .isNewDependencyPresent ) {
310+ doAfterVisit (new RemoveContentVisitor <>(t , true , true ));
311+ maybeUpdateModel ();
312+ return t ;
266313 }
267- return availableVersions .isEmpty () ? newVersion : max (availableVersions , versionComparator );
314+ if (isOldDependencyTag || isPluginDependencyTag (oldGroupId , oldArtifactId )) {
315+ if (acc .changeGroupId != null ) {
316+ t = changeChildTagValue (t , "groupId" , newGroupId , ctx );
317+ }
318+ if (acc .changeArtifactId != null ) {
319+ t = changeChildTagValue (t , "artifactId" , newArtifactId , ctx );
320+ }
321+
322+ if (acc .createVersion != null ) {
323+ Xml .Tag newVersionTag = Xml .Tag .build ("<version>" + acc .createVersion + "</version>" );
324+ t = (Xml .Tag ) new AddToTagVisitor <ExecutionContext >(t , newVersionTag , new MavenTagInsertionComparator (t .getChildren ())).visitNonNull (t , ctx , getCursor ().getParent ());
325+ } else if (acc .changeVersion != null ) {
326+ t = changeChildTagValue (t , "version" , acc .changeVersion , ctx );
327+ } else if (acc .removeVersionTag ) {
328+ t = (Xml .Tag ) new RemoveContentVisitor <>(t .getChild ("version" ).get (), false , true ).visitNonNull (t , ctx );
329+ }
330+
331+ if (t != tag ) {
332+ maybeUpdateModel ();
333+ }
334+ }
335+
336+ return t ;
268337 }
269338 };
270339 }
340+
341+ public static class Accumulator {
342+ Set <PomProperty > pomProperties = new HashSet <>();
343+ boolean isNewDependencyPresent ;
344+
345+ @ Nullable
346+ String changeGroupId ;
347+
348+ @ Nullable
349+ String changeArtifactId ;
350+
351+ boolean removeVersionTag ;
352+
353+ @ Nullable
354+ String createVersion ;
355+
356+ @ Nullable
357+ String changeVersion ;
358+ }
359+
360+ @ Value
361+ public static class PomProperty {
362+ Path filePath ;
363+ String name ;
364+ String value ;
365+ }
271366}
0 commit comments