package jenkins.plugins.nodejs.configfiles; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; import org.apache.commons.io.output.FileWriterWithEncoding; /** * Npm config file parser. * * @author Nikolas Falco * @since 1.0 */ public class Npmrc { private static final String UTF_8 = "UTF-8"; private Map<Object, String> properties = new LinkedHashMap<>(); /** * Parse the given file and store internally all user settings and * comments. * * @param file a valid npmrc user config file content. * @return the instance of parsed user config. * @throws IOException in case of I/O failure during file read */ public static Npmrc load(File file) throws IOException { if (file == null) { throw new NullPointerException("file is null"); } if (!file.isFile()) { throw new IllegalArgumentException("file " + file + " does not exists or is not file"); } Path path = Paths.get(file.getAbsolutePath()); String content = new String(Files.readAllBytes(path), UTF_8); Npmrc config = new Npmrc(); config.from(content); return config; } /** * Parse the given content and store internally all user settings and * comments. * * @param content a valid npmrc user config content. */ public void from(String content) { if (content == null) { return; } LineIterator iterator = IOUtils.lineIterator(new StringReader(content)); while (iterator.hasNext()) { String line = iterator.nextLine(); line = line.trim(); if (line.length() == 0) { continue; } if (line.startsWith(";")) { // comment addComment(line.substring(1)); } else { int eqIndex = line.indexOf('='); if (eqIndex != -1) { // NOSONAR String key = line.substring(0, eqIndex).trim(); String value = line.substring(eqIndex + 1).trim(); set(key, value); } else { // parse error addComment(line); } } } } /** * Add a comment line at the end of the file. * * @param comment the text content without the ';' prefix */ public void addComment(String comment) { properties.put(new Comment() {}, comment); } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Entry<Object, String> entry : properties.entrySet()) { String line; if (entry.getKey() instanceof Comment) { line = ';' + entry.getValue(); } else { line = entry.getKey() + " = " + entry.getValue(); } sb.append(line).append("\n"); } return sb.toString(); } /** * Write the content of user config to a file. * * @param file the destination file * @throws IOException in case of I/O write error */ public void save(File file) throws IOException { try (Writer writer = new FileWriterWithEncoding(file, UTF_8)) { IOUtils.write(toString(), writer); } } /** * Returns {@literal true} if this map contains a user config for the * specified key. * * @param key user setting whose presence in this config * @return {@literal true} if this config already contains the specified key */ public boolean contains(String key) { return properties.containsKey(key); } /** * Get the value for the specified property key. * * @param key user config entry key * @return the property value */ public String get(String key) { return properties.get(key); } /** * Set the value for the specified property key. If key already present it * will be override. * * @param key property key * @param value property value * @return the old value associated to the setting key, {@literal null} * otherwise */ public String set(String key, String value) { return properties.put(key, value); } /** * Set the value for the specified property key. If key already present it * will be override. * * @param key property key * @param value property value * @return {@literal false} the old value associated to the property key, * {@literal true} otherwise */ public boolean set(String key, boolean value) { return Boolean.parseBoolean(properties.put(key, Boolean.toString(value))); } /** * Get the value for the specified property key as a boolean. * * @param key user config entry key * @return a boolean represented by the property value or {@literal null} if * the key doesn't exist or the value associated is empty. */ public Boolean getAsBoolean(String key) { Boolean result = null; if (contains(key)) { result = Boolean.valueOf(properties.get(key)); } return result; } /** * Get the value for the specified property key as a number. * * @param key user config entry key * @return an integer represented by the property value or {@literal null} * if the key doesn't exist or the value associated is empty. */ public Integer getAsNumber(String key) { Integer result = null; if (contains(key)) { result = Integer.valueOf(properties.get(key)); } return result; } /** * Marker interface. * <p> * This class is intended to avoid collision if a special entry key was * choose to represent comment in the map. * * @author Nikolas Falco * */ private interface Comment { } }