/*******************************************************************************
* Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thomas Holland - initial API and implementation
*******************************************************************************/
package de.innot.avreclipse.core.targets;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import de.innot.avreclipse.AVRPlugin;
import de.innot.avreclipse.core.avrdude.ProgrammerConfig;
/**
* Manages the list of {@link TargetConfiguration} objects in the preferences.
* <p>
* This manager has methods to
* <ul>
* <li>get a configuration from the preferences: {@link #getConfig(String)},</li>
* <li>get a working copy configuration: {@link #getWorkingCopy(String)},</li>
* <li>create a new configuration: {@link #createNewConfig()},</li>
* <li>save a configuration: {@link #saveConfig(ProgrammerConfig)} and</li>
* <li>delete a configuration: {@link #deleteConfig(ProgrammerConfig)}</li>
* </p>
* <p>
* The manager also has methods to get a list of all available programmers and their names:
* {@link #getAllConfigIDs()} and {@link #getAllConfigNames()}.
* </p>
* <p>
* To improve access times all retrieved configurations are stored in an internal cache.
* </p>
* <p>
* This class implements the singleton pattern and can be accessed with the static
* {@link #getDefault()} method.
* </p>
*
* @author Thomas Holland
* @since 2.4
*
*/
public class TargetConfigurationManager {
/** Static singleton instance */
private static TargetConfigurationManager fInstance = null;
private IPath fStorageFolder;
/**
* The prefix for programmer configuration id values. This is appended with a running number to
* get the real id.
*/
private final static String CONFIG_PREFIX = "config.";
/** Cache of all Configs that have been used in this session */
private final Map<String, TargetConfiguration> fConfigsCache;
/**
* Gets the session <code>TargetConfigurationManager</code>.
*
* @return <code>TargetConfigurationManager</code> for the current Eclipse session.
*/
public static TargetConfigurationManager getDefault() {
if (fInstance == null) {
fInstance = new TargetConfigurationManager();
}
return fInstance;
}
// Private to prevent instantiation of this class.
private TargetConfigurationManager() {
fConfigsCache = new HashMap<String, TargetConfiguration>();
}
/**
* Create a new TargetConfiguration.
* <p>
* The returned TargetConfiguration is filled with some default values. It is immediately
* created in the preference store.
* </p>
*
* @return A new <code>ProgrammerConfig</code>
* @throws IOException
* An <code>IOException</code> is thrown when the new config file can not be
* created.
*/
public ITargetConfiguration createNewConfig() throws IOException {
// The id has the form "targetconfig.#" where # is a running
// number.
// Get all files in the folder and find the highest ID.
IPath folder = getConfigFolder();
File[] files = folder.toFile().listFiles();
int maxid = 0;
for (File file : files) {
if (file.isFile()) {
String name = file.getName();
if (name.startsWith(CONFIG_PREFIX)) {
int dot = name.lastIndexOf('.');
if (dot >= 0) {
String idstring = name.substring(dot + 1);
int id = Integer.parseInt(idstring);
if (id >= maxid) {
maxid = id + 1;
}
}
}
}
}
String filename = CONFIG_PREFIX + Integer.toString(maxid);
IPath newfile = new Path(folder.toString()).append(filename);
TargetConfiguration newconfig = new TargetConfiguration(newfile);
fConfigsCache.put(newconfig.getId(), newconfig);
return newconfig;
}
/**
* Deletes the given configuration.
*
* @param id
* The id of the target configuration to delete.
*/
public void deleteConfig(String id) throws IOException {
// If the config is in the cache, remove it from the cache
if (fConfigsCache.containsKey(id)) {
// First clear any listeners so that we don't have dangling references
TargetConfiguration tc = fConfigsCache.get(id);
tc.dispose();
fConfigsCache.remove(id);
}
// Delete the config file
File file = getConfigFolder().append(id).toFile();
if (file.exists()) {
if (!file.delete()) {
throw new IOException("Could not delete hardware config file '" + file.toString()
+ "'");
}
}
}
/**
* Get the {@link TargetConfiguration} with the given ID.
* <p>
* If the config has been requested before, a reference to the config in the internal cache is
* returned. All modifications to the returned config will affect the config in the cache.
* </p>
* <p>
* While these changes are only persisted when saveConfig() is called, it is usually better to
* use the {@link #getWorkingCopy(String)} call to get a safely modifiable config.
* </p>
*
* @see #getWorkingCopy(String)
*
* @param id
* <code>String</code> with an ID value.
* @return The requested <code>TargetConfiguration</code> or <code>null</code> if no config with
* the given ID exists.
* @throws IOException
* if a config file exists in the storage area, but could not be read.
*/
public ITargetConfiguration getConfig(String id) throws IOException {
return internalGetConfig(id);
}
private TargetConfiguration internalGetConfig(String id) throws IOException {
// Test for empty / null id
if (id == null || id.length() == 0) {
return null;
}
// Test if the config is already in the cache
if (fConfigsCache.containsKey(id)) {
return fConfigsCache.get(id);
}
// The config was not in the cache
// The file must exist, otherwise return null
IPath file = getConfigFolder().append(id);
if (!file.toFile().exists()) {
return null;
}
// Load the Config from the File
TargetConfiguration config = new TargetConfiguration(file);
fConfigsCache.put(id, config);
return config;
}
/**
* Get a working copy of the {@link TargetConfiguration} with the given Id.
* <p>
* The returned config is not backed by the cache, so any modifications will not be visible
* until the {@link #saveConfig(ProgrammerConfig) method is called with the returned config.<p>
* </p>
*
* @param sourceconfig
* Source <code>TargetConfiguration</code> to clone.
* @return New working copy of an existing configuration, or <code>null</code> if no config with the given id exists.
* @throws IOException when the source config exists, but can not be loaded from the storage area.
*/
public ITargetConfigurationWorkingCopy getWorkingCopy(String id) throws IOException {
// Clone the source config
TargetConfiguration sourceconfig = internalGetConfig(id);
if (sourceconfig == null) {
return null;
}
ITargetConfigurationWorkingCopy cloneconfig = new TargetConfiguration(sourceconfig);
return cloneconfig;
}
/**
* Checks if a target configuration with the given id exists.
*
* @param id
* A target configuration id string
* @return <code>true</code> if the configuration exists.
*/
public boolean exists(String id) {
// Test for empty / null id
if (id == null)
return false;
if (id.length() == 0)
return false;
// Test if the config is already in the cache
if (fConfigsCache.containsKey(id)) {
return true;
}
// The config was not in the cache
// The file must exist, otherwise return null
try {
File file = getConfigFolder().append(id).toFile();
return file.exists();
} catch (IOException ioe) {
return false;
}
}
/**
* Get a list of all available target configuration id's.
*
* @return List of all id strings
*/
public List<String> getConfigurationIDs() {
final List<String> confignames = new ArrayList<String>();
try {
File folder = getConfigFolder().toFile();
File[] allfiles = folder.listFiles();
for (File file : allfiles) {
String filename = file.getName();
if (filename.startsWith(CONFIG_PREFIX)) {
confignames.add(filename);
}
}
} catch (IOException ioe) {
// In case of errors return what is already in the list
}
return confignames;
}
private IPath getConfigFolder() throws IOException {
if (fStorageFolder == null) {
IPath location = AVRPlugin.getDefault().getStateLocation().append("hardwareconfigs");
File folder = location.toFile();
if (!folder.exists()) {
if (!folder.mkdirs()) {
throw new IOException("Could not create hardware config storage folder '"
+ folder.toString() + "'");
}
}
fStorageFolder = location;
}
return fStorageFolder;
}
}