Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ public class AbstractMavenFilteringRequest {
*/
private ChangeDetection changeDetection = ChangeDetection.CONTENT;

/**
* When {@code true}, the build fails if a filter expression cannot be resolved (i.e. the
* placeholder is left unsubstituted in the output). Defaults to {@code false} to preserve
* backward compatibility.
*
* @since 4.0.0-beta-4
*/
private boolean failOnMissingFilterValue = false;

/**
* Create instance.
*/
Expand Down Expand Up @@ -388,4 +397,26 @@ public ChangeDetection getChangeDetection() {
public void setChangeDetection(ChangeDetection changeDetection) {
this.changeDetection = requireNonNull(changeDetection);
}

/**
* Returns whether the build should fail when a filter expression cannot be resolved.
*
* @return {@code true} if an unresolved placeholder causes a build failure
* @since 4.0.0-beta-4
*/
public boolean isFailOnMissingFilterValue() {
return failOnMissingFilterValue;
}

/**
* Sets whether the build should fail when a filter expression cannot be resolved.
* When {@code true}, any placeholder that has no matching property causes a build failure
* instead of being passed through as-is. Defaults to {@code false}.
*
* @param failOnMissingFilterValue {@code true} to fail on unresolved placeholders
* @since 4.0.0-beta-4
*/
public void setFailOnMissingFilterValue(boolean failOnMissingFilterValue) {
this.failOnMissingFilterValue = failOnMissingFilterValue;
}
}
10 changes: 8 additions & 2 deletions src/main/java/org/apache/maven/shared/filtering/BaseFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ public List<FilterWrapper> getDefaultFilterWrappers(final AbstractMavenFiltering
request.getEscapeString(),
request.isEscapeWindowsPaths(),
request.isSupportMultiLineFiltering(),
request.getInterpolatorCustomizer());
request.getInterpolatorCustomizer(),
request.isFailOnMissingFilterValue());

defaultFilterWrappers.add(wrapper);

Expand Down Expand Up @@ -216,6 +217,8 @@ private static final class Wrapper extends FilterWrapper {

private final Consumer<Interpolator> interpolatorCustomizer;

private final boolean failOnMissingFilterValue;

Wrapper(
LinkedHashSet<String> delimiters,
Project project,
Expand All @@ -225,7 +228,8 @@ private static final class Wrapper extends FilterWrapper {
String escapeString,
boolean escapeWindowsPaths,
boolean supportMultiLineFiltering,
Consumer<Interpolator> interpolatorCustomizer) {
Consumer<Interpolator> interpolatorCustomizer,
boolean failOnMissingFilterValue) {
super();
this.delimiters = delimiters;
this.project = project;
Expand All @@ -236,6 +240,7 @@ private static final class Wrapper extends FilterWrapper {
this.escapeWindowsPaths = escapeWindowsPaths;
this.supportMultiLineFiltering = supportMultiLineFiltering;
this.interpolatorCustomizer = interpolatorCustomizer;
this.failOnMissingFilterValue = failOnMissingFilterValue;
}

@Override
Expand Down Expand Up @@ -268,6 +273,7 @@ public Reader getReader(Reader reader) {

filterReader.setInterpolateWithPrefixPattern(false);
filterReader.setEscapeString(escapeString);
filterReader.setFailOnMissingFilterValue(failOnMissingFilterValue);

return filterReader;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ public class InterpolatorFilterReaderLineEnding extends AbstractFilterReaderLine

private boolean eof = false;

/**
* When {@code true}, an unresolved filter expression causes an {@link IOException} instead of
* being passed through as-is. Defaults to {@code false}.
*
* @since 4.0.0-beta-4
*/
private boolean failOnMissingFilterValue = false;

/**
* @param in reader to use
* @param interpolator interpolator instance to use
Expand Down Expand Up @@ -306,9 +314,15 @@ public int read() throws IOException {
}

// write away the value if present, otherwise the key unmodified
if (value != null) {
// Note: the plexus interpolator never returns null — when a token is unresolved it
// returns the original expression (i.e. value.equals(key.toString())). We detect
// unresolved tokens by comparing value to the original key.
boolean resolved = value != null && !value.equals(key.toString());
if (resolved) {
replaceData = value;
replaceIndex = value.length();
} else if (failOnMissingFilterValue) {
throw new IOException("Unresolved filter token: '" + key + "'");
Comment thread
gnodet marked this conversation as resolved.
} else {
replaceData = key.toString();
replaceIndex = key.length();
Expand Down Expand Up @@ -349,4 +363,15 @@ public InterpolatorFilterReaderLineEnding setRecursionInterceptor(RecursionInter
this.recursionInterceptor = theRecursionInterceptor;
return this;
}

/**
* Sets whether the reader should throw an {@link IOException} when a filter expression cannot
* be resolved, instead of passing it through as-is. Defaults to {@code false}.
*
* @param failOnMissingFilterValue {@code true} to fail on unresolved placeholders
* @since 4.0.0-beta-4
*/
public void setFailOnMissingFilterValue(boolean failOnMissingFilterValue) {
this.failOnMissingFilterValue = failOnMissingFilterValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ public MavenResourcesExecution copyOf() {
mre.setDelimiters(new LinkedHashSet<>(this.getDelimiters()));
mre.setInterpolatorCustomizer(this.getInterpolatorCustomizer());
mre.setGracefulBinaryHandling(this.isGracefulBinaryHandling());
mre.setFailOnMissingFilterValue(this.isFailOnMissingFilterValue());
return mre;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ public class MultiDelimiterInterpolatorFilterReaderLineEnding extends AbstractFi

private boolean eof = false;

/**
* When {@code true}, an unresolved filter expression causes an {@link IOException} instead of
* being passed through as-is. Defaults to {@code false}.
*
* @since 4.0.0-beta-4
*/
private boolean failOnMissingFilterValue = false;

/**
* This constructor uses default begin token ${ and default end token }.
*
Expand Down Expand Up @@ -353,9 +361,15 @@ public int read() throws IOException {
}

// write away the value if present, otherwise the key unmodified
if (value != null) {
// Note: the plexus interpolator never returns null — when a token is unresolved it
// returns the original expression (i.e. value.equals(key.toString())). We detect
// unresolved tokens by comparing value to the original key.
boolean resolved = value != null && !value.equals(key.toString());
if (resolved) {
replaceData = value;
replaceIndex = value.length();
} else if (failOnMissingFilterValue) {
throw new IOException("Unresolved filter token: '" + key + "'");
} else {
replaceData = key.toString();
replaceIndex = key.length();
Expand Down Expand Up @@ -399,4 +413,15 @@ public AbstractFilterReaderLineEnding setRecursionInterceptor(RecursionIntercept
this.recursionInterceptor = givenRecursionInterceptor;
return this;
}

/**
* Sets whether the reader should throw an {@link IOException} when a filter expression cannot
* be resolved, instead of passing it through as-is. Defaults to {@code false}.
*
* @param failOnMissingFilterValue {@code true} to fail on unresolved placeholders
* @since 4.0.0-beta-4
*/
public void setFailOnMissingFilterValue(boolean failOnMissingFilterValue) {
this.failOnMissingFilterValue = failOnMissingFilterValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,25 @@
import java.io.StringReader;

import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.RecursionInterceptor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class InterpolatorFilterReaderLineEndingTest extends AbstractInterpolatorFilterReaderLineEndingTest {

@Mock
private Interpolator mockInterpolator;

@Override
protected Reader getAaaAaaReader(Reader in, Interpolator interpolator) {
return new InterpolatorFilterReaderLineEnding(in, interpolator, "aaa", "aaa", true);
Expand Down Expand Up @@ -80,4 +90,30 @@ public void setEscapeStringEmptyShouldDisableEscaping() throws Exception {
reader.setEscapeString("");
assertNull(reader.getEscapeString());
}

@Test
public void failOnMissingFilterValueThrowsWhenEnabled() throws Exception {
// mockInterpolator returns null → token is unresolved
when(mockInterpolator.interpolate(eq("${missing}"), eq(""), isA(RecursionInterceptor.class)))
.thenReturn(null);

InterpolatorFilterReaderLineEnding reader = new InterpolatorFilterReaderLineEnding(
new StringReader("value=${missing}"), mockInterpolator, "${", "}", true);
reader.setFailOnMissingFilterValue(true);

assertThrows(java.io.IOException.class, () -> IOUtils.toString(reader));
}

@Test
public void failOnMissingFilterValuePassesThroughWhenDisabled() throws Exception {
// mockInterpolator returns null → token is unresolved
when(mockInterpolator.interpolate(eq("${missing}"), eq(""), isA(RecursionInterceptor.class)))
.thenReturn(null);

InterpolatorFilterReaderLineEnding reader = new InterpolatorFilterReaderLineEnding(
new StringReader("value=${missing}"), mockInterpolator, "${", "}", true);
reader.setFailOnMissingFilterValue(false);

assertEquals("value=${missing}", IOUtils.toString(reader));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -132,4 +133,36 @@ void settingDelimiterSpecsRecalculatesMarkLength() {

assertEquals(markLength, reader.markLength, "mark length should reflect the current delimiter set");
}

@Test
void failOnMissingFilterValueThrowsWhenEnabled() throws Exception {
// interpolator returns null → token is unresolved
when(interpolator.interpolate(eq("${missing}"), isA(RecursionInterceptor.class)))
.thenReturn(null);

Reader in = new StringReader("value=${missing}");
MultiDelimiterInterpolatorFilterReaderLineEnding reader =
new MultiDelimiterInterpolatorFilterReaderLineEnding(in, interpolator, true);
reader.setDelimiterSpecs(Collections.singleton("${*}"));
reader.setInterpolateWithPrefixPattern(false);
reader.setFailOnMissingFilterValue(true);

assertThrows(java.io.IOException.class, () -> IOUtils.toString(reader));
}

@Test
void failOnMissingFilterValuePassesThroughWhenDisabled() throws Exception {
// interpolator returns null → token is unresolved
when(interpolator.interpolate(eq("${missing}"), isA(RecursionInterceptor.class)))
.thenReturn(null);

Reader in = new StringReader("value=${missing}");
MultiDelimiterInterpolatorFilterReaderLineEnding reader =
new MultiDelimiterInterpolatorFilterReaderLineEnding(in, interpolator, true);
reader.setDelimiterSpecs(Collections.singleton("${*}"));
reader.setInterpolateWithPrefixPattern(false);
reader.setFailOnMissingFilterValue(false);

assertEquals("value=${missing}", IOUtils.toString(reader));
}
}
Loading