PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/amps-maven-plugin/src/main/java/com/atlassian/maven/plugins/amps/util/ConfigFileUtils.java

https://bitbucket.org/atlassian/amps
Java | 267 lines | 162 code | 31 blank | 74 comment | 23 complexity | afb3c4a9ec5972f431a7b9aaa499bcac MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. package com.atlassian.maven.plugins.amps.util;
  2. import org.apache.maven.plugin.MojoExecutionException;
  3. import javax.annotation.ParametersAreNonnullByDefault;
  4. import java.io.File;
  5. import java.io.FileInputStream;
  6. import java.io.FileNotFoundException;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.io.OutputStream;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.Properties;
  14. import static java.nio.charset.StandardCharsets.UTF_8;
  15. import static java.util.Objects.hash;
  16. import static java.util.Objects.requireNonNull;
  17. import static org.apache.commons.io.FileUtils.readFileToString;
  18. import static org.apache.commons.io.FileUtils.writeStringToFile;
  19. /**
  20. * Utility methods relating to configuration files.
  21. */
  22. @ParametersAreNonnullByDefault
  23. public final class ConfigFileUtils {
  24. /**
  25. * Makes the given replacements within each of the given files.
  26. *
  27. * @param files the files to modify; any that don't exist are quietly skipped
  28. * @param replacements the replacements to make
  29. * @param inverted if you want to swap values with keys. Be aware that the list is processed in order,
  30. * so that if 2 keys have the same value, the first key will be chosen. The Replacement records with
  31. * reversible set to false will not be reversed. Default: false.
  32. * @throws MojoExecutionException if something goes wrong
  33. */
  34. public static void replace(final List<File> files, final List<Replacement> replacements, final boolean inverted)
  35. throws MojoExecutionException {
  36. for (File file : files) {
  37. if (file.exists()) {
  38. replace(file, replacements, inverted);
  39. }
  40. }
  41. }
  42. private static void replace(final File cfgFile, final List<Replacement> replacements, final boolean inverted)
  43. throws MojoExecutionException {
  44. try {
  45. String config = readFileToString(cfgFile, UTF_8);
  46. if (!inverted) {
  47. for (Replacement replacement : replacements) {
  48. if (replacement.applyWhenUnzipping()) {
  49. config = replacement.replace(config);
  50. }
  51. }
  52. } else {
  53. for (Replacement replacement : replacements) {
  54. config = replacement.reverse(config);
  55. }
  56. }
  57. writeStringToFile(cfgFile, config, UTF_8);
  58. } catch (IOException ex) {
  59. throw new MojoExecutionException("Unable to replace " + cfgFile, ex);
  60. }
  61. }
  62. /**
  63. * Modifies the given file by replacing the given pattern with the given replacement.
  64. *
  65. * @param file the file to modify; if this does not exist or is a directory, this method quietly does nothing
  66. * @param pattern the pattern to replace
  67. * @param replacement the replacement
  68. * @throws MojoExecutionException if there's an I/O problem
  69. * @see String#replaceAll(String, String) for how the pattern and replacement work
  70. */
  71. public static void replaceAll(final File file, final String pattern, final String replacement)
  72. throws MojoExecutionException {
  73. if (!file.isFile()) {
  74. return;
  75. }
  76. try {
  77. final String oldContents = readFileToString(file, UTF_8);
  78. final String newContents = oldContents.replaceAll(pattern, replacement); // uses regex
  79. writeStringToFile(file, newContents, UTF_8);
  80. } catch (IOException ex) {
  81. throw new MojoExecutionException("Unable to replace " + file, ex);
  82. }
  83. }
  84. /**
  85. * Updates the given properties file with the given map of properties.
  86. *
  87. * @param propertiesFile the properties file to update
  88. * @param newProperties the properties to add/update in the file
  89. */
  90. public static void updateProperties(final File propertiesFile, final Map<String, String> newProperties) {
  91. final Properties props = readProperties(propertiesFile);
  92. newProperties.forEach(props::setProperty);
  93. try (final OutputStream out = new FileOutputStream(propertiesFile)) {
  94. props.store(out, "Processed by AMPS");
  95. } catch (IOException e) {
  96. throw new IllegalStateException(e);
  97. }
  98. }
  99. private static Properties readProperties(final File propertiesFile) {
  100. final Properties props = new Properties();
  101. try (final InputStream in = new FileInputStream(propertiesFile)) {
  102. props.load(in);
  103. } catch (final FileNotFoundException ignored) {
  104. // Ignore
  105. } catch (IOException e) {
  106. throw new IllegalStateException(e);
  107. }
  108. return props;
  109. }
  110. /**
  111. * Represents a replacement in a configuration file or set of files.
  112. */
  113. public static class Replacement implements Comparable<Replacement> {
  114. /**
  115. * Factory method for a {@link Replacement} that applies only when <em>unzipping</em> the home directory.
  116. *
  117. * @param key the key
  118. * @param value the value
  119. * @return a new instance
  120. */
  121. public static Replacement onlyWhenUnzipping(final String key, final String value) {
  122. return new Replacement(key, value, true, false);
  123. }
  124. private final String key;
  125. private final String value;
  126. /**
  127. * Replace the key with the value when unzipping a home. This is the normal meaning of
  128. * a replacement, {@code key -> value}
  129. */
  130. private final boolean applyWhenUnzipping;
  131. /**
  132. * Detect the value and replace it with the key when zipping a home directory
  133. */
  134. private final boolean reversible;
  135. /**
  136. * Represents a key to be replaced in the configuration files.
  137. * <p>
  138. * <p/>
  139. * <b>Important</b>: If your value is short, such as "/", "", "true", "false", please set reversible=false.
  140. * When zipping a home, config files are parsed and everything is replaced back with keys, such as %PRODUCT_HOME_DIR%.
  141. * If you provide a string with false positives, you may parametrise too many variables.
  142. *
  143. * @param key the key to be replaced. Must not be null.
  144. * @param value the value to be replaced. Must not be null. <b>Important</b>: If short, such as / or "", please set reversible=false.
  145. */
  146. public Replacement(final String key, final String value) {
  147. this(key, value, true);
  148. }
  149. /**
  150. * Represents a key to be replaced in the configuration files.
  151. *
  152. * @param key the key to be replaced. Must not be null.
  153. * @param value the value to be replaced. Must not be null.
  154. * @param reversible true if the value should be replaced with the key before
  155. * preparing a snapshot. Default is true. Use false when:<ul>
  156. * <li>the value is non-unique, e.g. "%BAMBOO_ENABLED% = true" should not be reversible.</li>
  157. * <li>we only support the value for legacy, but we wouldn't re-wrap a snapshot with this key</li>
  158. * </ul>
  159. */
  160. public Replacement(final String key, final String value, final boolean reversible) {
  161. this(key, value, true, reversible);
  162. }
  163. /**
  164. * @param key the key, never null
  165. * @param value the value, never null
  166. * @param applyWhenUnzipping apply when unzipping a home. Defaults to true.
  167. * @param reversible apply when zipping a home. Defaults to true.
  168. */
  169. Replacement(final String key, final String value, final boolean applyWhenUnzipping, final boolean reversible) {
  170. this.key = requireNonNull(key, "key must not be null");
  171. this.value = requireNonNull(value, "value must not be null");
  172. this.applyWhenUnzipping = applyWhenUnzipping;
  173. this.reversible = reversible;
  174. }
  175. public final String replace(final String s) {
  176. return replace(s, key, value);
  177. }
  178. public final String reverse(final String s) {
  179. return reversible ? replace(s, value, key) : s;
  180. }
  181. protected String replace(final String s, final String target, final String replacement) {
  182. return s.replace(target, replacement);
  183. }
  184. public boolean applyWhenUnzipping() {
  185. return applyWhenUnzipping;
  186. }
  187. @Override
  188. public boolean equals(final Object other) {
  189. if (this == other) {
  190. return true;
  191. }
  192. if (other == null || getClass() != other.getClass()) {
  193. return false;
  194. }
  195. final Replacement that = (Replacement) other;
  196. return applyWhenUnzipping == that.applyWhenUnzipping &&
  197. reversible == that.reversible &&
  198. key.equals(that.key) &&
  199. value.equals(that.value);
  200. }
  201. @Override
  202. public int hashCode() {
  203. return hash(key, value, applyWhenUnzipping, reversible);
  204. }
  205. @Override
  206. public int compareTo(final Replacement other) {
  207. int length1 = this.value.length();
  208. int length2 = other.value.length();
  209. return length2 - length1;
  210. }
  211. @Override
  212. public String toString() {
  213. final String operation;
  214. if (applyWhenUnzipping && reversible) {
  215. operation = " <-> ";
  216. } else if (applyWhenUnzipping) {
  217. operation = " -> ";
  218. } else if (reversible) {
  219. operation = " <- ";
  220. } else {
  221. operation = " (nop) ";
  222. }
  223. return key + operation + value;
  224. }
  225. }
  226. public static final class RegexReplacement extends Replacement {
  227. public RegexReplacement(final String key, final String value) {
  228. super(key, value, false);
  229. }
  230. @Override
  231. protected String replace(final String s, final String target, final String replacement) {
  232. return s.replaceAll(target, replacement);
  233. }
  234. }
  235. private ConfigFileUtils() {}
  236. }