Package org.openpnp.model

Source Code of org.openpnp.model.Configuration

/*
   Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
  
   This file is part of OpenPnP.
  
  OpenPnP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenPnP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenPnP.  If not, see <http://www.gnu.org/licenses/>.
  
   For more information about OpenPnP visit http://openpnp.org
*/

package org.openpnp.model;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.prefs.Preferences;

import org.apache.commons.io.FileUtils;
import org.openpnp.ConfigurationListener;
import org.openpnp.spi.Machine;
import org.openpnp.util.ResourceUtils;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.convert.AnnotationStrategy;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.stream.Format;
import org.simpleframework.xml.stream.HyphenStyle;
import org.simpleframework.xml.stream.Style;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Configuration extends AbstractModelObject {
  private static final Logger logger = LoggerFactory.getLogger(Configuration.class);
 
  private static Configuration instance;
 
  private static final String PREF_UNITS = "Configuration.units";
  private static final String PREF_UNITS_DEF = "Millimeters";
 
  private static final String PREF_LENGTH_DISPLAY_FORMAT = "Configuration.lengthDisplayFormat";
  private static final String PREF_LENGTH_DISPLAY_FORMAT_DEF = "%.3f";
 
  private static final String PREF_LENGTH_DISPLAY_FORMAT_WITH_UNITS = "Configuration.lengthDisplayFormatWithUnits";
  private static final String PREF_LENGTH_DISPLAY_FORMAT_WITH_UNITS_DEF = "%.3f%s";
 
  private static final String PREF_VERTICAL_SCROLL_UNIT_INCREMENT = "Configuration.verticalScrollUnitIncrement";
  private static final int PREF_VERTICAL_SCROLL_UNIT_INCREMENT_DEF = 16;
 
  private LinkedHashMap<String, Package> packages = new LinkedHashMap<String, Package>();
  private LinkedHashMap<String, Part> parts = new LinkedHashMap<String, Part>();
  private Machine machine;
  private LinkedHashMap<File, Board> boards = new LinkedHashMap<File, Board>();
  private boolean dirty;
  private boolean loaded;
  private Set<ConfigurationListener> listeners = Collections.synchronizedSet(new HashSet<ConfigurationListener>());
  private File configurationDirectory;
  private Preferences prefs;
 
  public static Configuration get() {
    if (instance == null) {
      throw new Error("Configuration instance not yet initialized.");
    }
    return instance;
  }
 
  public static synchronized void initialize(File configurationDirectory) {
    instance = new Configuration(configurationDirectory);
    instance.setLengthDisplayFormatWithUnits(PREF_LENGTH_DISPLAY_FORMAT_WITH_UNITS_DEF);
  }
 
  private Configuration(File configurationDirectory) {
    this.configurationDirectory = configurationDirectory;
    this.prefs = Preferences.userNodeForPackage(Configuration.class);
  }
 
  public File getConfigurationDirectory() {
      return configurationDirectory;
  }
 
  public LengthUnit getSystemUnits() {
    return LengthUnit.valueOf(prefs.get(PREF_UNITS, PREF_UNITS_DEF));
  }
 
  public void setSystemUnits(LengthUnit lengthUnit) {
    prefs.put(PREF_UNITS, lengthUnit.name());
  }
 
  public String getLengthDisplayFormat() {
    return prefs.get(PREF_LENGTH_DISPLAY_FORMAT, PREF_LENGTH_DISPLAY_FORMAT_DEF);
  }
 
  public void setLengthDisplayFormat(String format) {
    prefs.put(PREF_LENGTH_DISPLAY_FORMAT, format);
  }
 
  public String getLengthDisplayFormatWithUnits() {
    return prefs.get(PREF_LENGTH_DISPLAY_FORMAT_WITH_UNITS, PREF_LENGTH_DISPLAY_FORMAT_WITH_UNITS_DEF);
  }
 
  public void setLengthDisplayFormatWithUnits(String format) {
    prefs.put(PREF_LENGTH_DISPLAY_FORMAT_WITH_UNITS, format);
  }
 
  public int getVerticalScrollUnitIncrement() {
    return prefs.getInt(PREF_VERTICAL_SCROLL_UNIT_INCREMENT, PREF_VERTICAL_SCROLL_UNIT_INCREMENT_DEF);
  }
 
  public void setVerticalScrollUnitIncrement(int verticalScrollUnitIncrement) {
    prefs.putInt(PREF_VERTICAL_SCROLL_UNIT_INCREMENT, PREF_VERTICAL_SCROLL_UNIT_INCREMENT_DEF);
  }
 
  /**
   * Gets a File reference for the resources directory belonging to the
   * given class. The directory is guaranteed to exist.
   * @param forClass
   * @return
   * @throws IOException
   */
  public File getResourceDirectory(Class forClass) throws IOException {
    File directory = new File(configurationDirectory, forClass.getCanonicalName());
    if (!directory.exists()) {
      directory.mkdirs();
    }
    return directory;
  }
 
  /**
   * Gets a File reference for the named file within the configuration
   * directory. forClass is used to uniquely identify the file and keep it
   * separate from other classes' files.
   * @param forClass
   * @param name
   * @return
   */
  public File getResourceFile(Class forClass, String name) throws IOException {
    return new File(getResourceDirectory(forClass), name);
  }
 
  /**
   * Creates a new file with a unique name within the configuration
   * directory. forClass is used to uniquely identify the file within
   * the application and a unique name is generated within that namespace.
   * suffix is appended to the unique part of the filename. The result of
   * calling File.getName() on the returned file can be used to load the
   * same file in the future by calling getResourceFile().
   * This method uses File.createTemporaryFile() and so the rules for that
   * method must be followed when calling this one.
   * @param forClass
   * @param suffix
   * @return
   * @throws IOException
   */
  public File createResourceFile(Class forClass, String prefix, String suffix) throws IOException {
    File directory = new File(configurationDirectory, forClass.getCanonicalName());
    if (!directory.exists()) {
      directory.mkdirs();
    }
    File file = File.createTempFile(prefix, suffix, directory);
    return file;
  }
 
  public boolean isDirty() {
    return dirty;
  }

  public void setDirty(boolean dirty) {
    this.dirty = dirty;
  }

  public void addListener(ConfigurationListener listener) {
    listeners.add(listener);
    if (loaded) {
        try {
            listener.configurationLoaded(this);
            listener.configurationComplete(this);
        }
        catch (Exception e) {
            // TODO: Need to find a way to raise this to the GUI
            throw new Error(e);
        }
    }
  }
 
  public void removeListener(ConfigurationListener listener) {
    listeners.remove(listener);
  }
 
  public void load() throws Exception {
    boolean forceSave = false;
    boolean overrideUserConfig = Boolean.getBoolean("overrideUserConfig");
   
    try {
      File file = new File(configurationDirectory, "packages.xml");
      if (overrideUserConfig || !file.exists()) {
        logger.info("No packages.xml found in configuration directory, loading defaults.");
        file = File.createTempFile("packages", "xml");
        FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/packages.xml"), file);
        forceSave = true;
      }
      loadPackages(file);
    }
    catch (Exception e) {
      String message = e.getMessage();
      if (e.getCause() != null && e.getCause().getMessage() != null) {
        message = e.getCause().getMessage();
      }
      throw new Exception("Error while reading packages.xml (" + message + ")", e);
    }
   
   
    try {
      File file = new File(configurationDirectory, "parts.xml");
      if (overrideUserConfig || !file.exists()) {
        logger.info("No parts.xml found in configuration directory, loading defaults.");
        file = File.createTempFile("parts", "xml");
        FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/parts.xml"), file);
        forceSave = true;
      }
      loadParts(file);
    }
    catch (Exception e) {
      String message = e.getMessage();
      if (e.getCause() != null && e.getCause().getMessage() != null) {
        message = e.getCause().getMessage();
      }
      throw new Exception("Error while reading parts.xml (" + message + ")", e);
    }
   
   
    try {
      File file = new File(configurationDirectory, "machine.xml");
      if (overrideUserConfig || !file.exists()) {
        logger.info("No machine.xml found in configuration directory, loading defaults.");
        file = File.createTempFile("machine", "xml");
        FileUtils.copyURLToFile(ClassLoader.getSystemResource("config/machine.xml"), file);
        forceSave = true;
      }
      loadMachine(file);
    }
    catch (Exception e) {
      String message = e.getMessage();
      if (e.getCause() != null && e.getCause().getMessage() != null) {
        message = e.getCause().getMessage();
      }
      throw new Exception("Error while reading machine.xml (" + message + ")", e);
    }
   
        loaded = true;

        for (ConfigurationListener listener : listeners) {
            listener.configurationLoaded(this);
        }

        if (forceSave) {
      logger.info("Defaults were loaded. Saving to configuration directory.");
      configurationDirectory.mkdirs();
      save();
    }
       
    for (ConfigurationListener listener : listeners) {
            listener.configurationComplete(this);
        }
  }
 
  public void save() throws Exception {
    try {
      saveMachine(new File(configurationDirectory, "machine.xml"));
    }
    catch (Exception e) {
      throw new Exception("Error while saving machine.xml (" + e.getMessage() + ")", e);
    }
    try {
      savePackages(new File(configurationDirectory, "packages.xml"));
    }
    catch (Exception e) {
      throw new Exception("Error while saving packages.xml (" + e.getMessage() + ")", e);
    }
    try {
      saveParts(new File(configurationDirectory, "parts.xml"));
    }
    catch (Exception e) {
      throw new Exception("Error while saving parts.xml (" + e.getMessage() + ")", e);
    }
    dirty = false;
  }
 
  public Package getPackage(String id) {
    if (id == null) {
      return null;
    }
    return packages.get(id.toUpperCase());
  }
 
  public List<Package> getPackages() {
    return Collections.unmodifiableList(new ArrayList<Package>(packages.values()));
  }
 
  public void addPackage(Package pkg) {
      if (null == pkg.getId()) {
          throw new Error("Package with null Id cannot be added to Configuration.");
      }
    packages.put(pkg.getId().toUpperCase(), pkg);
    dirty = true;
    firePropertyChange("packages", null, packages);
  }
 
  public Part getPart(String id) {
    if (id == null) {
      return null;
    }
    return parts.get(id.toUpperCase());
  }
 
  public List<Part> getParts() {
    return Collections.unmodifiableList(new ArrayList<Part>(parts.values()));
  }
 
  public void addPart(Part part) {
      if (null == part.getId()) {
            throw new Error("Part with null Id cannot be added to Configuration.");
      }
    parts.put(part.getId().toUpperCase(), part);
    dirty = true;
    firePropertyChange("parts", null, parts);
  }
 
  public List<Board> getBoards() {
    return Collections.unmodifiableList(new ArrayList<Board>(boards.values()));
  }
 
  public Machine getMachine() {
    return machine;
  }
 
  public Board getBoard(File file) throws Exception {
    if (!file.exists()) {
      Board board = new Board(file);
      board.setName(file.getName());
      Serializer serializer = createSerializer();
      serializer.write(board, file);
    }
    file = file.getCanonicalFile();
    if (boards.containsKey(file)) {
      return boards.get(file);
    }
    Board board = loadBoard(file);
    boards.put(file, board);
    firePropertyChange("boards", null, boards);
    return board;
  }
 
  private void loadMachine(File file) throws Exception {
    Serializer serializer = createSerializer();
    MachineConfigurationHolder holder = serializer.read(MachineConfigurationHolder.class, file);
    machine = holder.machine;
  }
 
  private void saveMachine(File file) throws Exception {
    MachineConfigurationHolder holder = new MachineConfigurationHolder();
    holder.machine = machine;
    Serializer serializer = createSerializer();
    serializer.write(holder, new ByteArrayOutputStream());
    serializer.write(holder, file);
  }
 
  private void loadPackages(File file) throws Exception {
    Serializer serializer = createSerializer();
    PackagesConfigurationHolder holder = serializer.read(PackagesConfigurationHolder.class, file);
    for (Package pkg : holder.packages) {
      addPackage(pkg);
    }
  }
 
  private void savePackages(File file) throws Exception {
    Serializer serializer = createSerializer();
    PackagesConfigurationHolder holder = new PackagesConfigurationHolder();
    holder.packages = new ArrayList<Package>(packages.values());
    serializer.write(holder, new ByteArrayOutputStream());
    serializer.write(holder, file);
  }
 
  private void loadParts(File file) throws Exception {
    Serializer serializer = createSerializer();
    PartsConfigurationHolder holder = serializer.read(PartsConfigurationHolder.class, file);
    for (Part part : holder.parts) {
      addPart(part);
    }
  }
 
  private void saveParts(File file) throws Exception {
    Serializer serializer = createSerializer();
    PartsConfigurationHolder holder = new PartsConfigurationHolder();
    holder.parts = new ArrayList<Part>(parts.values());
    serializer.write(holder, new ByteArrayOutputStream());
    serializer.write(holder, file);
  }
 
  public Job loadJob(File file) throws Exception {
    Serializer serializer = createSerializer();
    Job job = serializer.read(Job.class, file);
    job.setFile(file);
   
    // Once the Job is loaded we need to resolve any Boards that it
    // references.
    for (BoardLocation boardLocation : job.getBoardLocations()) {
      String boardFilename = boardLocation.getBoardFile();
      // First see if we can find the board at the given filename
      // If the filename is not absolute this will be relative
      // to the working directory
      File boardFile = new File(boardFilename);
      if (!boardFile.exists()) {
        // If that fails, see if we can find it relative to the
        // directory the job was in
        boardFile = new File(file.getParentFile(), boardFilename);
      }
      if (!boardFile.exists()) {
        throw new Exception("Board file not found: " + boardFilename);
      }
      Board board = getBoard(boardFile);
      boardLocation.setBoard(board);
    }
   
    job.setDirty(false);
   
    return job;
  }
 
  public void saveJob(Job job, File file) throws Exception {
    Serializer serializer = createSerializer();
    Set<Board> boards = new HashSet<Board>();
    // Fix the paths to any boards in the Job
    for (BoardLocation boardLocation : job.getBoardLocations()) {
      Board board = boardLocation.getBoard();
      boards.add(board);
      try {
        String relativePath = ResourceUtils.getRelativePath(
            board.getFile().getAbsolutePath(),
            file.getAbsolutePath(),
            File.separator);
        boardLocation.setBoardFile(relativePath);
      }
      catch (ResourceUtils.PathResolutionException ex) {
        boardLocation.setBoardFile(board.getFile().getAbsolutePath());
      }
    }
    // Save the job
    serializer.write(job, new ByteArrayOutputStream());
    serializer.write(job, file);
    job.setFile(file);
    job.setDirty(false);
  }
 
  public void saveBoard(Board board) throws Exception {
    Serializer serializer = createSerializer();
    serializer.write(board, new ByteArrayOutputStream());
    serializer.write(board, board.getFile());
    board.setDirty(false);
  }
 
  private Board loadBoard(File file) throws Exception {
    Serializer serializer = createSerializer();
    Board board = serializer.read(Board.class, file);
    board.setFile(file);
    board.setDirty(false);
    return board;
  }
 
  public static Serializer createSerializer() {
    Style style = new HyphenStyle();
    Format format = new Format(style);
    AnnotationStrategy strategy = new AnnotationStrategy();
    Serializer serializer = new Persister(strategy, format);
    return serializer;
  }
 
  /**
   * Used to provide a fixed root for the Machine when serializing.
   */
  @Root(name="openpnp-machine")
  public static class MachineConfigurationHolder {
    @Element
    private Machine machine;
  }
 
  /**
   * Used to provide a fixed root for the Packages when serializing.
   */
  @Root(name="openpnp-packages")
  public static class PackagesConfigurationHolder {
    @ElementList(inline=true, entry="package", required=false)
    private ArrayList<Package> packages = new ArrayList<Package>();
  }
 
  /**
   * Used to provide a fixed root for the Parts when serializing.
   */
  @Root(name="openpnp-parts")
  public static class PartsConfigurationHolder {
    @ElementList(inline=true, entry="part", required=false)
    private ArrayList<Part> parts = new ArrayList<Part>();
  }
}
TOP

Related Classes of org.openpnp.model.Configuration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.