Skip to content

Commit 1d84b3d

Browse files
author
TheSnoozer
committed
#18: add support for xml
1 parent 8c24df0 commit 1d84b3d

File tree

4 files changed

+181
-15
lines changed

4 files changed

+181
-15
lines changed

src/main/java/pl/project13/core/CommitIdPropertiesOutputFormat.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,8 @@ public enum CommitIdPropertiesOutputFormat {
3030
* Indicator to generate a json file.
3131
*/
3232
JSON,
33+
/**
34+
* Indicator to generate a xml file.
35+
*/
36+
XML,
3337
}

src/main/java/pl/project13/core/PropertiesFileGenerator.java

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import pl.project13.core.util.BuildFileChangeListener;
2323
import pl.project13.core.util.JsonManager;
2424
import pl.project13.core.util.PropertyManager;
25+
import pl.project13.core.util.XmlManager;
2526

2627
import javax.annotation.Nonnull;
2728
import java.io.*;
@@ -56,20 +57,27 @@ public void maybeGeneratePropertiesFile(
5657
) throws GitCommitIdExecutionException {
5758
try {
5859
final File gitPropsFile = craftPropertiesOutputFile(projectDir, propsFile);
59-
final boolean isJsonFormat = CommitIdPropertiesOutputFormat.JSON.equals(propertiesOutputFormat);
60-
6160
boolean shouldGenerate = true;
6261

6362
if (gitPropsFile.exists()) {
6463
final Properties persistedProperties;
6564

6665
try {
67-
if (isJsonFormat) {
68-
log.info(String.format("Reading existing json file [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
69-
persistedProperties = JsonManager.readJsonProperties(gitPropsFile, sourceCharset);
70-
} else {
71-
log.info(String.format("Reading existing properties file [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
72-
persistedProperties = PropertyManager.readProperties(gitPropsFile);
66+
switch (propertiesOutputFormat) {
67+
case JSON:
68+
log.info(String.format("Reading existing json file [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
69+
persistedProperties = JsonManager.readJsonProperties(gitPropsFile, sourceCharset);
70+
break;
71+
case PROPERTIES:
72+
log.info(String.format("Reading existing properties file [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
73+
persistedProperties = PropertyManager.readProperties(gitPropsFile);
74+
break;
75+
case XML:
76+
log.info(String.format("Reading existing xml file [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
77+
persistedProperties = XmlManager.readXmlProperties(gitPropsFile, sourceCharset);
78+
break;
79+
default:
80+
throw new GitCommitIdExecutionException("Not implemented:" + propertiesOutputFormat);
7381
}
7482

7583
final Properties propertiesCopy = (Properties) localProperties.clone();
@@ -92,13 +100,23 @@ public void maybeGeneratePropertiesFile(
92100
try (OutputStream outputStream = new FileOutputStream(gitPropsFile)) {
93101
OrderedProperties sortedLocalProperties = PropertiesFileGenerator.createOrderedProperties();
94102
localProperties.forEach((key, value) -> sortedLocalProperties.setProperty((String) key, (String) value));
95-
if (isJsonFormat) {
96-
log.info(String.format("Writing json file to [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
97-
JsonManager.dumpJson(outputStream, sortedLocalProperties, sourceCharset);
98-
} else {
99-
log.info(String.format("Writing properties file to [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
100-
// using outputStream directly instead of outputWriter this way the UTF-8 characters appears in unicode escaped form
101-
PropertyManager.dumpProperties(outputStream, sortedLocalProperties, escapeUnicode);
103+
switch (propertiesOutputFormat) {
104+
case JSON:
105+
log.info(String.format("Writing json file to [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
106+
JsonManager.dumpJson(outputStream, sortedLocalProperties, sourceCharset);
107+
break;
108+
case PROPERTIES:
109+
log.info(String.format("Writing properties file to [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
110+
// using outputStream directly instead of outputWriter this way the UTF-8 characters appears in unicode escaped form
111+
PropertyManager.dumpProperties(outputStream, sortedLocalProperties, escapeUnicode);
112+
break;
113+
case XML:
114+
log.info(String.format("Writing xml file to [%s] (for module %s)...", gitPropsFile.getAbsolutePath(), projectName));
115+
// using outputStream directly instead of outputWriter this way the UTF-8 characters appears in unicode escaped form
116+
XmlManager.dumpXml(outputStream, sortedLocalProperties, sourceCharset);
117+
break;
118+
default:
119+
throw new GitCommitIdExecutionException("Not implemented:" + propertiesOutputFormat);
102120
}
103121
} catch (final IOException ex) {
104122
throw new RuntimeException("Cannot create custom git properties file: " + gitPropsFile, ex);
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* This file is part of git-commit-id-plugin-core by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
3+
*
4+
* git-commit-id-plugin-core is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* git-commit-id-plugin-core is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with git-commit-id-plugin-core. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package pl.project13.core.util;
19+
20+
import nu.studer.java.util.OrderedProperties;
21+
import pl.project13.core.CannotReadFileException;
22+
23+
import javax.annotation.Nonnull;
24+
import javax.xml.XMLConstants;
25+
import javax.xml.stream.*;
26+
import java.io.*;
27+
import java.nio.charset.Charset;
28+
import java.nio.charset.StandardCharsets;
29+
import java.util.Map;
30+
import java.util.Properties;
31+
32+
public class XmlManager {
33+
public static void dumpXml(OutputStream outputStream, OrderedProperties sortedLocalProperties, Charset sourceCharset) throws IOException {
34+
/*
35+
TODO get rid of custom indents and use
36+
https://ewernli.com/2009/06/18/stax-pretty-printer/
37+
https://stackoverflow.com/a/38371920
38+
*/
39+
XMLOutputFactory fac = XMLOutputFactory.newInstance();
40+
41+
try (Writer outputWriter = new OutputStreamWriter(outputStream, sourceCharset)) {
42+
XMLStreamWriter writer = fac.createXMLStreamWriter(outputWriter);
43+
// <?xml version="1.0" encoding="UTF-8"?>
44+
writer.writeStartDocument(StandardCharsets.UTF_8.toString(), "1.0");
45+
46+
writer.writeStartElement("gitCommitIdPlugin");
47+
writer.writeCharacters("\n"); // indents
48+
49+
for (Map.Entry e : sortedLocalProperties.entrySet()) {
50+
writer.writeCharacters(" "); // indents
51+
// <property key="git.branch" value="master"/>
52+
writer.writeEmptyElement("property");
53+
writer.writeAttribute("key", e.getKey().toString());
54+
writer.writeAttribute("value", e.getValue().toString());
55+
writer.writeCharacters("\n"); // indents
56+
}
57+
writer.writeCharacters("\n"); // indents
58+
writer.writeEndElement(); // </gitCommitIdPlugin>
59+
60+
writer.writeEndDocument();
61+
writer.flush();
62+
writer.close();
63+
} catch (XMLStreamException e) {
64+
throw new RuntimeException(e);
65+
}
66+
67+
}
68+
69+
public static Properties readXmlProperties(@Nonnull File xmlFile, Charset sourceCharset) throws CannotReadFileException {
70+
Properties retVal = new Properties();
71+
72+
try (FileInputStream fis = new FileInputStream(xmlFile)) {
73+
try (InputStreamReader reader = new InputStreamReader(fis, sourceCharset)) {
74+
XMLInputFactory factory = XMLInputFactory.newInstance();
75+
76+
// https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html#Java
77+
// factory.setProperty("http://apache.org/xml/features/disallow-doctype-decl", true); // not supported :/
78+
// https://docs.oracle.com/en/java/javase/11/security/java-api-xml-processing-jaxp-security-guide.html#JSSEC-GUID-88B04BE2-35EF-4F61-B4FA-57A0E9102342
79+
// Feature for Secure Processing (FSP)
80+
// factory.setProperty(XMLConstants.FEATURE_SECURE_PROCESSING, true); // not supported :/
81+
// https://rules.sonarsource.com/java/RSPEC-2755
82+
// to be compliant, completely disable DOCTYPE declaration:
83+
factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
84+
// or completely disable external entities declarations:
85+
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
86+
// or prohibit the use of all protocols by external entities:
87+
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
88+
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
89+
90+
// Other things we don't need
91+
factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
92+
factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
93+
factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
94+
95+
XMLStreamReader xmlReader = factory.createXMLStreamReader(reader);
96+
while (xmlReader.hasNext()) {
97+
if (xmlReader.next() == XMLStreamConstants.START_ELEMENT) {
98+
if (xmlReader.getLocalName().equals("property")) {
99+
String key = xmlReader.getAttributeValue(null, "key");
100+
String value = xmlReader.getAttributeValue(null, "value");
101+
retVal.setProperty(key, value);
102+
}
103+
}
104+
}
105+
} catch (XMLStreamException ex) {
106+
throw new CannotReadFileException(ex);
107+
}
108+
} catch (IOException e) {
109+
throw new CannotReadFileException(e);
110+
}
111+
return retVal;
112+
}
113+
}

src/test/java/pl/project13/core/GitCommitIdPluginIntegrationTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.junit.runner.RunWith;
2727
import pl.project13.core.git.GitDescribeConfig;
2828
import pl.project13.core.util.JsonManager;
29+
import pl.project13.core.util.XmlManager;
2930

3031
import javax.annotation.Nonnull;
3132
import java.io.File;
@@ -460,8 +461,38 @@ public void shouldGenerateCustomPropertiesFileJson(boolean useNativeGit) throws
460461
assertThat(targetFilePath).exists();
461462
Properties p = JsonManager.readJsonProperties(targetFilePath, StandardCharsets.UTF_8);
462463
assertThat(p.size() > 10);
464+
Assert.assertEquals(p, properties);
463465
}
464466

467+
@Test
468+
@Parameters(method = "useNativeGit")
469+
public void shouldGenerateCustomPropertiesFileXml(boolean useNativeGit) throws Exception {
470+
// given
471+
File dotGitDirectory = createTmpDotGitDirectory(AvailableGitTestRepo.WITH_ONE_COMMIT_WITH_SPECIAL_CHARACTERS);
472+
473+
File targetFilePath = sandbox.resolve("custom-git.xml").toFile();
474+
targetFilePath.delete();
475+
476+
GitCommitIdPlugin.Callback cb =
477+
new GitCommitIdTestCallback()
478+
.setDotGitDirectory(dotGitDirectory)
479+
.setUseNativeGit(useNativeGit)
480+
.setShouldGenerateGitPropertiesFile(true)
481+
.setGenerateGitPropertiesFilename(targetFilePath)
482+
.setPropertiesOutputFormat(CommitIdPropertiesOutputFormat.XML)
483+
.build();
484+
Properties properties = new Properties();
485+
486+
// when
487+
GitCommitIdPlugin.runPlugin(cb, properties);
488+
// then
489+
assertThat(targetFilePath).exists();
490+
Properties p = XmlManager.readXmlProperties(targetFilePath, StandardCharsets.UTF_8);
491+
assertThat(p.size() > 10);
492+
Assert.assertEquals(p, properties);
493+
}
494+
495+
465496
@Test
466497
@Parameters(method = "useNativeGit")
467498
public void shouldGenerateDescribeWithTagOnlyWhenForceLongFormatIsFalse(boolean useNativeGit) throws Exception {

0 commit comments

Comments
 (0)