FileSettings.java

/*******************************************************************************
 * Copyright (c) 2004, 2013 Steve Flasby
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * <ul>
 *     <li>Redistributions of source code must retain the above copyright notice,
 *         this list of conditions and the following disclaimer.</li>
 *     <li>Redistributions in binary form must reproduce the above copyright notice,
 *         this list of conditions and the following disclaimer in the documentation
 *         and/or other materials provided with the distribution.</li>
 * </ul>
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/

package org.flasby.settings;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;

/*
 * @author Steve Flasby
 * Oct 8, 2012
 */

public class FileSettings implements PersistentSettings {

  Properties mySettings = new Properties();

  private final Path path;

  public FileSettings(String userSettingsFileName, boolean mustExist) throws IOException {
    this(Paths.get(System.getProperty("user.home"), userSettingsFileName), mustExist);
  }

  public FileSettings(String userSettingsFileName) throws IOException {
    this(userSettingsFileName, false);
  }

  public FileSettings(Path path) throws IOException {
    this(path, false);
  }

  public FileSettings(Path path, boolean mustExist) throws IOException {
    this.path = path;
    if (path.toFile().exists()) {
      try (InputStream is = Files.newInputStream(path)) {
        mySettings.load(is);
      }
    } else if (mustExist) {
      throw new NoSuchFileException("Unable to load file: " + path);
    }
  }

  @Override
  public String get(String name) {
    return mySettings.getProperty(name);
  }

  @Override
  public void add(String name, String value) {
    mySettings.put(name, value);
  }

  @Override
  public void remove(String Name) {
    mySettings.remove(Name);
  }

  @Override
  public void save() throws IOException {
    Path parent = path.getParent();
    if (parent == null) {
      throw new InvalidPathException(
          path.toString(), "Can't find the parent of this file, please check your settings");
    }
    Files.createDirectories(parent);
    Path bak = path.resolveSibling(path.getFileName() + ".bak");
    if (Files.exists(path)) {
      Files.move(path, bak, StandardCopyOption.REPLACE_EXISTING);
    }
    try (OutputStream os = Files.newOutputStream(path)) {
      mySettings.store(os, path.toFile().getName());
    }
  }

  @Override
  public Iterator<Entry<String, String>> iterator() {
    HashMap<String, String> s = new HashMap<>();

    for (String key : mySettings.stringPropertyNames()) {
      s.put(key, mySettings.getProperty(key));
    }
    return s.entrySet().iterator();
  }

  /**
   * deletes the underlying file used to store the Settings. After this call all save() operations
   * will throw exceptions.
   *
   * @throws IOException
   */
  public void delete() throws IOException {
    Files.deleteIfExists(path);
  }

  @Override
  public boolean isEmpty() {
    return mySettings.isEmpty();
  }

  @Override
  public String toString() {
    return "FilesSettings:" + path.toAbsolutePath();
  }

  @Override
  public void doIfExists(String key, ApplyWithExistingValue ifExists) {
    if (get(key) != null) {
      ifExists.apply(key, get(key));
    }
  }

  @Override
  public void doIfExists(
      String key, ApplyWithExistingValue ifExists, ApplyWithMissingKey otherwise) {
    if (get(key) != null) {
      ifExists.apply(key, get(key));
    } else {
      otherwise.apply(key);
    }
  }
}