Skip to content

Commit 93c7f7f

Browse files
authored
feat: Add charSet parameter to FILE macro (#144)
* feat: Add ability to set the character set for reading the file. Defaults to the default charset for the running JVM * Fix spotbugs
1 parent a987834 commit 93c7f7f

File tree

5 files changed

+96
-21
lines changed

5 files changed

+96
-21
lines changed

src/main/java/org/jenkinsci/plugins/tokenmacro/impl/WorkspaceFileMacro.java

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,18 @@
3131
import hudson.model.TaskListener;
3232

3333
import java.io.BufferedReader;
34+
import java.io.ByteArrayOutputStream;
3435
import java.io.IOException;
3536
import java.io.InputStreamReader;
37+
import java.io.OutputStreamWriter;
38+
import java.io.StringWriter;
3639
import java.nio.charset.Charset;
3740
import java.util.Collections;
3841
import java.util.List;
42+
import java.util.stream.Collectors;
3943

44+
import org.apache.commons.io.IOUtils;
45+
import org.apache.commons.lang.ObjectUtils;
4046
import org.jenkinsci.plugins.tokenmacro.DataBoundTokenMacro;
4147
import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException;
4248

@@ -48,6 +54,8 @@ public class WorkspaceFileMacro extends DataBoundTokenMacro {
4854
public String fileNotFoundMessage = "ERROR: File '%s' does not exist";
4955
@Parameter
5056
public int maxLines = -1;
57+
@Parameter
58+
public String charSet = Charset.defaultCharset().name();
5159

5260

5361
public static final String MACRO_NAME = "FILE";
@@ -83,28 +91,13 @@ public String evaluate(Run<?,?> run, FilePath workspace, TaskListener listener,
8391
}
8492

8593
try {
86-
if(maxLines > 0) {
87-
int lines = 0;
88-
89-
StringBuilder result = new StringBuilder();
90-
BufferedReader reader = null;
91-
try {
92-
String line;
93-
reader = new BufferedReader(new InputStreamReader(workspace.child(path).read(), Charset.defaultCharset()));
94-
while (lines < maxLines && (line = reader.readLine()) != null) {
95-
result.append(line);
96-
result.append('\n');
97-
lines++;
98-
}
99-
} finally {
100-
if(reader != null) {
101-
reader.close();
102-
}
94+
Charset charset = Charset.forName(charSet);
95+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(workspace.child(path).read(), charset))) {
96+
if (maxLines > 0) {
97+
return reader.lines().limit(maxLines).collect(Collectors.joining("\n"));
98+
} else {
99+
return reader.lines().collect(Collectors.joining("\n"));
103100
}
104-
105-
return result.toString();
106-
} else {
107-
return workspace.child(path).readToString();
108101
}
109102
} catch (IOException e) {
110103
return "ERROR: File '" + path + "' could not be read";

src/test/java/org/jenkinsci/plugins/tokenmacro/impl/WorkspaceFileMacroTest.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,23 @@
88
import hudson.model.FreeStyleProject;
99
import hudson.model.TaskListener;
1010
import hudson.util.StreamTaskListener;
11+
import org.apache.commons.io.IOUtils;
1112
import org.jvnet.hudson.test.TestBuilder;
1213

14+
import java.io.BufferedReader;
1315
import java.io.BufferedWriter;
1416
import java.io.IOException;
17+
import java.io.InputStreamReader;
1518
import java.io.OutputStreamWriter;
19+
import java.util.HashMap;
20+
import java.util.Map;
1621

1722
import org.junit.Rule;
1823
import org.junit.Test;
1924
import org.jvnet.hudson.test.JenkinsRule;
2025

2126
import static org.junit.Assert.*;
27+
import static org.mockito.Mockito.when;
2228

2329
/**
2430
* @author Kohsuke Kawaguchi
@@ -77,4 +83,75 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
7783
assertEquals("Hello, world! " + i, lines[i]);
7884
}
7985
}
86+
87+
@Test
88+
public void testUtfEncodings() throws Exception {
89+
String[] expected = { "première is first",
90+
"première is slightly different",
91+
"Кириллица is Cyrillic",
92+
"\uD801\uDC00 am Deseret" };
93+
94+
String[] encodings = { "utf8", "utf16" };
95+
96+
for(String encoding : encodings) {
97+
FreeStyleProject project = j.createFreeStyleProject();
98+
TaskListener listener = StreamTaskListener.fromStdout();
99+
project.getBuildersList().add(new TestBuilder() {
100+
@Override
101+
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
102+
FilePath file = build.getWorkspace().child(encoding + ".txt");
103+
try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(file.write(), encoding))) {
104+
try(BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(encoding + ".txt"), encoding))) {
105+
IOUtils.copy(reader, writer);
106+
}
107+
}
108+
return true;
109+
}
110+
});
111+
FreeStyleBuild build = project.scheduleBuild2(0).get();
112+
113+
WorkspaceFileMacro content = new WorkspaceFileMacro();
114+
content.path = encoding + ".txt";
115+
content.charSet = encoding;
116+
117+
String output = content.evaluate(build, listener, WorkspaceFileMacro.MACRO_NAME);
118+
String[] lines = output.split("\n");
119+
assertEquals(4, lines.length);
120+
121+
for (int i = 0; i < 4; i++) {
122+
assertEquals(expected[i], lines[i]);
123+
}
124+
}
125+
}
126+
127+
@Test
128+
public void testSimpleChinese() throws Exception {
129+
FreeStyleProject project = j.createFreeStyleProject();
130+
TaskListener listener = StreamTaskListener.fromStdout();
131+
132+
String expected;
133+
try(BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("Chinese-Simplified.txt"), "utf16"))) {
134+
expected = IOUtils.toString(reader);
135+
}
136+
project.getBuildersList().add(new TestBuilder() {
137+
@Override
138+
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
139+
FilePath file = build.getWorkspace().child("Chinese-Simplified.txt");
140+
try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(file.write(), "utf16"))) {
141+
try(BufferedReader reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("Chinese-Simplified.txt"), "utf16"))) {
142+
IOUtils.copy(reader, writer);
143+
}
144+
}
145+
return true;
146+
}
147+
});
148+
FreeStyleBuild build = project.scheduleBuild2(0).get();
149+
150+
WorkspaceFileMacro content = new WorkspaceFileMacro();
151+
content.path = "Chinese-Simplified.txt";
152+
content.charSet = "utf16";
153+
154+
String output = content.evaluate(build, listener, WorkspaceFileMacro.MACRO_NAME);
155+
assertEquals(expected, output);
156+
}
80157
}

src/test/resources/org/jenkinsci/plugins/tokenmacro/impl/Chinese-Simplified.txt

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Binary file not shown.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
première is first
2+
première is slightly different
3+
Кириллица is Cyrillic
4+
𐐀 am Deseret

0 commit comments

Comments
 (0)