Skip to content

Commit

Permalink
#342: Adding the possibility of adding new scm tags
Browse files Browse the repository at this point in the history
  • Loading branch information
jarmoniuk committed Sep 16, 2022
1 parent 4637b2d commit fd4f0b1
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 42 deletions.
22 changes: 14 additions & 8 deletions src/main/java/org/codehaus/mojo/versions/SetScmTagMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,33 +93,39 @@ protected void update( ModifiedPomXMLEventReader pom )
List<String> failures = new ArrayList<>();
if ( !isBlank( newTag ) )
{
getLog().info( "Updating tag: " + scm.getTag() + " -> " + newTag );
if ( !PomHelper.setProjectValue( pom, "/project/scm/tag", newTag ) )
getLog().info( "Updating tag: " + ( scm != null && scm.getTag() != null
? scm.getTag() : "(empty)" ) + " -> " + newTag );
if ( !PomHelper.setElementValue( pom, "/project/scm", "tag", newTag ) )
{
failures.add( "tag: " + newTag );
}
}
if ( !isBlank( connection ) )
{
getLog().info( "Updating connection: " + scm.getConnection() + " -> " + connection );
if ( !PomHelper.setProjectValue( pom, "/project/scm/connection", connection ) )
getLog().info( "Updating connection: " + ( scm != null && scm.getConnection() != null
? scm.getConnection() : "(empty)" ) + " -> " + connection );
if ( !PomHelper.setElementValue( pom, "/project/scm", "connection", connection ) )
{
failures.add( "connection: " + connection );
}
}
if ( !isBlank( developerConnection ) )
{
getLog().info( "Updating developerConnection: " + scm.getDeveloperConnection() + " -> "
getLog().info( "Updating developerConnection: "
+ ( scm != null && scm.getDeveloperConnection() != null
? scm.getDeveloperConnection() : "(empty)" ) + " -> "
+ developerConnection );
if ( !PomHelper.setProjectValue( pom, "/project/scm/developerConnection", developerConnection ) )
if ( !PomHelper.setElementValue( pom, "/project/scm", "developerConnection",
developerConnection ) )
{
failures.add( "developerConnection: " + developerConnection );
}
}
if ( !isBlank( url ) )
{
getLog().info( "Updating url: " + scm.getUrl() + " -> " + url );
if ( !PomHelper.setProjectValue( pom, "/project/scm/url", url ) )
getLog().info( "Updating url: " + ( scm != null && scm.getUrl() != null
? scm.getUrl() : "(empty)" ) + " -> " + url );
if ( !PomHelper.setElementValue( pom, "/project/scm", "url", url ) )
{
failures.add( "url: " + url );
}
Expand Down
163 changes: 131 additions & 32 deletions src/main/java/org/codehaus/mojo/versions/api/PomHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import static java.util.stream.IntStream.range;

/**
* Helper class for modifying pom files.
*
Expand Down Expand Up @@ -232,59 +234,156 @@ else if ( matchScopeRegex.matcher( path ).matches() )
public static boolean setProjectVersion( final ModifiedPomXMLEventReader pom, final String value )
throws XMLStreamException
{
return setProjectValue( pom, "/project/version", value );
return setElementValue( pom, "/project", "version", value, false );
}

/**
* Searches the pom re-defining a project value using the given pattern.
* Sets the value of the given element given its parent element path.
* Will only consider the first found occurrence of the parent element.
* If the element is not found in the parent element, the method will create the element.
*
* @param pom The pom to modify.
* @param pattern The pattern to look for.
* @param value The new value of the property.
* @return <code>true</code> if a replacement was made.
* @throws XMLStreamException if something went wrong.
* @param pom pom to modify
* @param parentPath path of the parent element
* @param elementName name of the element to set or create
* @param value the new value of the element
* @return {@code true} if the element was created or replaced
* @throws XMLStreamException if something went wrong
*/
public static boolean setProjectValue( final ModifiedPomXMLEventReader pom, String pattern, final String value )
throws XMLStreamException
public static boolean setElementValue( ModifiedPomXMLEventReader pom, String parentPath,
String elementName, String value )
throws XMLStreamException
{
Stack<String> stack = new Stack<>();
String path = "";
final Pattern matchScopeRegex;
boolean madeReplacement = false;
matchScopeRegex = Pattern.compile( pattern );

pom.rewind();
return setElementValue( pom, parentPath, elementName, value, true );
}

while ( pom.hasNext() )
/**
* Sets the value of the given element given its parent element path.
* Will only consider the first found occurrence of the parent element.
* If the element is not found in the parent element, the method will create the element
* if {@code shouldCreate} is {@code true}.
*
* @param pom pom to modify
* @param parentPath path of the parent element
* @param elementName name of the element to set or create
* @param value the new value of the element
* @param shouldCreate should the element be created if it's not found in the first encountered parent element
* matching the parentPath
* @return {@code true} if the element was created or replaced
* @throws XMLStreamException if something went wrong
*/
public static boolean setElementValue( ModifiedPomXMLEventReader pom, String parentPath,
String elementName, String value, boolean shouldCreate )
throws XMLStreamException
{
class ElementValueInternal
{
XMLEvent event = pom.nextEvent();
if ( event.isStartElement() )
private final String parentName;
private final String superParentPath;

private static final int MARK_CHILD_BEGIN = 0;
private static final int MARK_OPTION = 1;
private static final int PARENT_BEGIN = 2;

ElementValueInternal()
{
stack.push( path );
path = path + "/" + event.asStartElement().getName().getLocalPart();
int lastDelimeterIndex = parentPath.lastIndexOf( '/' );
parentName = parentPath.substring( lastDelimeterIndex + 1 );
superParentPath = parentPath.substring( 0, lastDelimeterIndex );
}

if ( matchScopeRegex.matcher( path ).matches() )
boolean process( String currentPath ) throws XMLStreamException
{
boolean replacementMade = false;
while ( !replacementMade && pom.hasNext() )
{
pom.mark( 0 );
XMLEvent event = pom.nextEvent();
if ( event.isStartElement() )
{
String currentElementName = event.asStartElement().getName().getLocalPart();

// here, we will only mark the beginning of the child or the parent element
if ( currentPath.equals( parentPath ) && elementName.equals( currentElementName ) )
{
pom.mark( MARK_CHILD_BEGIN );
}
else if ( currentPath.equals( superParentPath ) && currentElementName.equals( parentName ) )
{
pom.mark( PARENT_BEGIN );
}
// process child element
replacementMade = process( currentPath + "/" + currentElementName );
}
else if ( event.isEndElement() )
{
// here we're doing the replacement
if ( currentPath.equals( parentPath + "/" + elementName ) )
{
// end of the child
replaceValueInChild();
replacementMade = true;
}
else if ( shouldCreate && currentPath.equals( parentPath ) )
{
// end of the parent
replaceValueInParent();
replacementMade = true;
}
else
{
return false;
}
}
}
return replacementMade;
}
if ( event.isEndElement() )

private void replaceValueInChild()
{
if ( matchScopeRegex.matcher( path ).matches() )
pom.mark( MARK_OPTION );
if ( pom.getBetween( MARK_CHILD_BEGIN, MARK_OPTION ).length() > 0 )
{
pom.mark( 1 );
if ( pom.hasMark( 0 ) && pom.hasMark( 1 ) )
pom.replaceBetween( 0, 1, value );
}
else
{
pom.replace( String.format( "<%1$s>%2$s</%1$s>", elementName, value ) );
}
}

private void replaceValueInParent()
{
pom.mark( MARK_OPTION );
if ( pom.hasMark( PARENT_BEGIN ) )
{
if ( pom.getBetween( PARENT_BEGIN, MARK_OPTION ).length() > 0 )
{
pom.replaceBetween( 0, 1, value );
madeReplacement = true;
pom.replace( String.format( "<%2$s>%3$s</%2$s></%1$s>",
parentName, elementName, value ) );
}
else
{
pom.replace( String.format( "<%1$s><%2$s>%3$s</%2$s></%1$s>",
parentName, elementName, value ) );
}
pom.clearMark( 0 );
pom.clearMark( 1 );
}
path = stack.pop();
else
{
pom.replace( String.format( "<%1$s><%2$s>%3$s</%2$s></%1$s>",
parentName, elementName, value ) );
}
}
}
return madeReplacement;

try
{
pom.rewind();
return new ElementValueInternal().process( "" );
}
finally
{
range( 0, 3 ).forEach( pom::clearMark );
}
}

/**
Expand Down
57 changes: 57 additions & 0 deletions src/test/java/org/codehaus/mojo/versions/SetScmTagMojoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.codehaus.mojo.versions;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.codehaus.mojo.versions.utils.BaseMojoTestCase;
import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.matchesPattern;

/**
* Basic tests for {@linkplain SetPropertyMojoTest}.
*
* @author Andrzej Jarmoniuk
*/
public class SetScmTagMojoTest extends BaseMojoTestCase
{
@Test
public void testNewScmValues() throws Exception
{
Path pomFile = Paths.get( "target/test-classes/org/codehaus/mojo/set-scm-tag/issue-342-pom.xml" );
createMojo( "set-scm-tag", pomFile.toString() )
.execute();
String output = String.join( "", Files.readAllLines( pomFile ) )
.replaceAll( "\\s*", "" );
assertThat( output, allOf(
matchesPattern( ".*<scm>.*<tag>\\s*newTag\\s*</tag>.*</scm>.*" ),
matchesPattern( ".*<scm>.*<url>\\s*url\\s*</url>.*</scm>.*" ),
matchesPattern( ".*<scm>.*<connection>\\s*connection\\s*</connection>.*</scm>.*" ),
matchesPattern( ".*<scm>.*<developerConnection>\\s*"
+ "developerConnection\\s*</developerConnection>.*</scm>.*" )
)
);
}
}
Loading

0 comments on commit fd4f0b1

Please sign in to comment.