/*******************************************************************************
* 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.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ListenerList;
import de.innot.avreclipse.core.avrdude.AVRDudeException;
/**
* Implementation of the ITargetConfiguration API.
* <p>
* This class implements both the {@link ITargetConfiguration} and
* {@link ITargetConfigurationWorkingCopy} interfaces, so it acts as both.
* </p>
* <p>
* This class may not be instantiated directly by clients. Instances are created and managed by the
* {@link TargetConfigurationManager}.
* </p>
*
* @author Thomas Holland
* @since 2.4
*
*/
public class TargetConfiguration implements ITargetConfiguration, ITargetConfigurationWorkingCopy,
ITargetConfigConstants {
private final static String EMPTY_STRING = "";
private File fPropertiesFile;
private String fId;
private boolean fDirty;
/** Flag to indicate that the config has been disposed. */
private boolean fIsDisposed = false;
/** The Properties container for all attributes. */
private Properties fAttributes = new Properties();
/** Map of all attributes to their default values. */
private Map<String, String> fDefaults = new HashMap<String, String>();
/**
* List of registered listeners (element type: <code>ITargetConfigChangeListener</code>). These
* listeners are to be informed when the current value of an attribute changes.
*/
protected ListenerList fListeners = new ListenerList();
/** The source target configuration if this is a working copy */
private TargetConfiguration fOriginal;
/** The current programmer tool for this target configuration. */
private IProgrammerTool fProgrammerTool;
/** The current gdbserver tool for this target configuration. */
private IGDBServerTool fGDBServerTool;
/** Map of the owners for attributes not handled by the this hardware configuration itself. */
private Map<String, ITargetConfigurationTool> fAttributeOwner = new HashMap<String, ITargetConfigurationTool>();
private TargetConfiguration() {
initDefaults();
}
/**
* Instantiate a new target configuration from a given file.
* <p>
* If the file already exists, then it is loaded. Otherwise the standard attributes are set to
* the default values.
* </p>
*
* @param path
* handle to the file containing the hardware configuration attributes
* @throws IOException
* thrown if the file exists, but can not be read.
*/
protected TargetConfiguration(IPath path) throws IOException {
this();
fPropertiesFile = path.toFile();
fId = path.lastSegment();
if (fPropertiesFile.exists()) {
// If the hardware configuration file already exists we just load it
load(fPropertiesFile);
} else {
// This is a brand new hardware configuration.
// We set all attributes to their defaults and then save them.
// need to pull in the defaults from the currently selected tools first.
getProgrammerTool();
getGDBServerTool();
// Now we can set all defaults.
restoreDefaults();
// Immediately save the file to create the file
save(fPropertiesFile, true);
}
}
/**
* Make a Working copy of the given <code>TargetConfiguration</code>.
* <p>
* The copy can be safely modified without affecting the source until the {@link #doSave()}
* method is called, which will then copy all changes to the source configuration.
* </p>
*
* @param config
*/
protected TargetConfiguration(TargetConfiguration config) {
this();
fOriginal = config;
fId = config.fId;
fPropertiesFile = config.fPropertiesFile;
loadFromConfig(config);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getId()
*/
public String getId() {
return fId;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getName()
*/
public String getName() {
return getAttribute(ATTR_NAME);
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setName(java.lang.String)
*/
public void setName(String name) {
setAttribute(ATTR_NAME, name);
}
/**
* Get the user supplied description of the target configuration.
*
* @return the Name
*/
public String getDescription() {
return getAttribute(ATTR_DESCRIPTION);
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setDescription(java.lang
* .String)
*/
public void setDescription(String description) {
setAttribute(ATTR_DESCRIPTION, description);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getMCU()
*/
public String getMCU() {
return getAttribute(ATTR_MCU);
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setMCU(java.lang.String)
*/
public void setMCU(String mcuid) {
setAttribute(ATTR_MCU, mcuid);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getFCPU()
*/
public int getFCPU() {
String fcpu = getAttribute(ATTR_FCPU);
return Integer.parseInt(fcpu);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setFCPU(int)
*/
public void setFCPU(int fcpu) {
String value = Integer.toString(fcpu);
setAttribute(ATTR_FCPU, value);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getSupportedMCUs(boolean)
*/
public Set<String> getSupportedMCUs(boolean filtered) {
IProgrammerTool progtool = getProgrammerTool();
IGDBServerTool gdbserver = getGDBServerTool();
Set<String> allmcus = new HashSet<String>();
Set<String> progtoolmcus = null;
Set<String> gdbservermcus = null;
try {
progtoolmcus = progtool.getMCUs();
} catch (AVRDudeException e) {
// in case of an exception we just leave the Set at null
// so it won't be used
}
try {
gdbservermcus = gdbserver.getMCUs();
} catch (AVRDudeException e) {
// in case of an exception we just leave the Set at null
// so it won't be used
}
if (progtoolmcus != null) {
allmcus.addAll(progtoolmcus);
}
if (gdbservermcus != null) {
if (filtered && progtoolmcus != null) {
allmcus.retainAll(gdbservermcus);
} else {
allmcus.addAll(gdbservermcus);
}
}
return allmcus;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getSupportedProgrammers(boolean)
*/
public Set<String> getAllProgrammers(boolean supported) {
IProgrammerTool progtool = getProgrammerTool();
IGDBServerTool gdbserver = getGDBServerTool();
Set<String> allprogrammers = new HashSet<String>();
Set<String> progtoolprogrammers = null;
Set<String> gdbserverprogrammers = null;
try {
progtoolprogrammers = progtool.getProgrammers();
} catch (AVRDudeException e) {
// in case of an exception we just leave the Set at null
// so it won't be used
}
try {
gdbserverprogrammers = gdbserver.getProgrammers();
} catch (AVRDudeException e) {
// in case of an exception we just leave the Set at null
// so it won't be used
}
if (progtoolprogrammers != null) {
allprogrammers.addAll(progtoolprogrammers);
}
if (gdbserverprogrammers != null) {
if (supported && progtoolprogrammers != null) {
allprogrammers.retainAll(gdbserverprogrammers);
} else {
allprogrammers.addAll(gdbserverprogrammers);
}
}
return allprogrammers;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getProgrammer(java.lang.String)
*/
public IProgrammer getProgrammer(String programmerid) {
// first check if the currently selected programmer tool knows the id
// As this will usually be avrdude the chances are high that it knows
// the programmer.
try {
IProgrammer progger = getProgrammerTool().getProgrammer(programmerid);
if (progger != null) {
return progger;
}
} catch (AVRDudeException ade) {
// continue with the gdbserver
}
// The programmer tool didn't know the id. Maybe the gdbserver knows it.
try {
IProgrammer progger = getGDBServerTool().getProgrammer(programmerid);
if (progger != null) {
return progger;
}
} catch (AVRDudeException ade) {
// continue with the other tools
}
// Nope. Lets go through all known tools to find one that knows this id.
List<String> alltools = ToolManager.getDefault().getAllTools(null);
for (String toolid : alltools) {
try {
ITargetConfigurationTool tool = ToolManager.getDefault().getTool(this, toolid);
IProgrammer progger = tool.getProgrammer(programmerid);
if (progger != null) {
return progger;
}
} catch (AVRDudeException ade) {
// just continue with the next tool
}
}
// Nothing found
// TODO: Maybe return a special "unknown" programmer.
return null;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getProgrammerTool()
*/
public IProgrammerTool getProgrammerTool() {
if (fProgrammerTool == null) {
// create the programmer tool if it has not yet been done.
String id = getAttribute(ATTR_PROGRAMMER_TOOL_ID);
ITargetConfigurationTool tool = ToolManager.getDefault().getTool(this, id);
if (tool instanceof IProgrammerTool) {
fProgrammerTool = (IProgrammerTool) tool;
} else {
// TODO throw an exception
fProgrammerTool = null;
}
registerTool(fProgrammerTool);
}
return fProgrammerTool;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setProgrammerTool(java.lang
* .String)
*/
public void setProgrammerTool(String toolid) {
// nothing to do if the tool is not changed
if (fProgrammerTool != null && (fProgrammerTool.getId().equals(toolid))) {
return;
}
// Check if the id is a valid programmer tool id
List<String> programmerids = ToolManager.getDefault().getAllTools(
ToolManager.AVRPROGRAMMERTOOL);
if (programmerids.contains(toolid)) {
// yes, id is valid. Get the actual tool and register its attributes
ITargetConfigurationTool tool = ToolManager.getDefault().getTool(this, toolid);
registerTool(tool);
fProgrammerTool = (IProgrammerTool) tool;
setAttribute(ATTR_PROGRAMMER_TOOL_ID, tool.getId());
} else {
throw new IllegalArgumentException("Invalid tool id '" + toolid + "'");
}
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getGDBServerTool()
*/
public IGDBServerTool getGDBServerTool() {
if (fGDBServerTool == null) {
// create the gdbserver tool if it has not yet been done.
String id = getAttribute(ATTR_GDBSERVER_ID);
ITargetConfigurationTool tool = ToolManager.getDefault().getTool(this, id);
if (tool instanceof IGDBServerTool) {
fGDBServerTool = (IGDBServerTool) tool;
registerTool(fGDBServerTool);
} else {
// TODO throw an exception
fGDBServerTool = null;
}
}
return fGDBServerTool;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setGDBServerTool(java.lang
* .String)
*/
public void setGDBServerTool(String toolid) {
// nothing to do if the tool is not changed
if (fGDBServerTool != null && (fGDBServerTool.getId().equals(toolid))) {
return;
}
// Check if the id is a valid gdbserver tool id
List<String> programmerids = ToolManager.getDefault().getAllTools(ToolManager.AVRGDBSERVER);
if (programmerids.contains(toolid)) {
// yes, id is valid. Get the actual tool and register its attributes
ITargetConfigurationTool tool = ToolManager.getDefault().getTool(this, toolid);
registerTool(tool);
fGDBServerTool = (IGDBServerTool) tool;
setAttribute(ATTR_PROGRAMMER_TOOL_ID, tool.getId());
} else {
throw new IllegalArgumentException("Invalid tool id '" + toolid + "'");
}
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getAttribute(java.lang.String,
* java.lang.String)
*/
public String getAttribute(String attributeName) {
Assert.isNotNull(attributeName);
String value = fAttributes.getProperty(attributeName);
if (value == null) {
value = fDefaults.get(attributeName);
if (value == null) {
value = EMPTY_STRING;
}
}
return value;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setAttribute(java.lang.String
* , java.lang.String)
*/
public void setAttribute(String attributeName, String newvalue) {
Assert.isNotNull(newvalue);
Assert.isNotNull(attributeName);
String oldvalue = fAttributes.getProperty(attributeName);
if (oldvalue == null || !oldvalue.equals(newvalue)) {
// only change attribute & fire event if the value is actually changed
fAttributes.setProperty(attributeName, newvalue);
fireAttributeChangeEvent(attributeName, oldvalue, newvalue);
fDirty = true;
}
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfiguration#getBooleanAttribute(java.lang.String)
*/
public boolean getBooleanAttribute(String attribute) {
Assert.isNotNull(attribute);
String value = getAttribute(attribute);
boolean boolvalue = Boolean.parseBoolean(value);
return boolvalue;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setBooleanAttribute(java
* .lang.String, boolean)
*/
public void setBooleanAttribute(String attribute, boolean value) {
String valuestring = Boolean.toString(value);
setAttribute(attribute, valuestring);
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfiguration#getBooleanAttribute(java.lang.String)
*/
public int getIntegerAttribute(String attribute) {
Assert.isNotNull(attribute);
String value = getAttribute(attribute);
if (value.length() == 0) {
return -1;
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException nfe) {
return 0;
}
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setIntegerAttribute(java
* .lang.String, int)
*/
public void setIntegerAttribute(String attribute, int value) {
String valuestring = Integer.toString(value);
setAttribute(attribute, valuestring);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#getAttributes()
*/
public Map<String, String> getAttributes() {
HashMap<String, String> map = new HashMap<String, String>();
for (Object obj : fAttributes.keySet()) {
String key = (String) obj;
String value = fAttributes.getProperty(key);
map.put(key, value);
}
return map;
}
private void registerTool(ITargetConfigurationTool tool) {
// Safety check
if (tool == null) {
return;
}
// Get all attributes supported by the tool, and add their default values to our own
// internal list. Also remember that this tool is a handler for all its attributes.
String[] toolattrs = tool.getAttributes();
for (String attr : toolattrs) {
fDefaults.put(attr, tool.getDefaultValue(attr));
fAttributeOwner.put(attr, tool);
}
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#isDirty()
*/
public boolean isDirty() {
return fDirty;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#doSave()
*/
public synchronized void doSave() throws IOException {
save(fPropertiesFile, false);
}
private void save(File file, boolean force) throws IOException {
// Saving a disposed config is not allowed, as it could overwrite a new config with the same
// id.
if (fIsDisposed) {
throw new IllegalStateException("Config is disposed");
}
if (fDirty || force) {
FileWriter reader = new FileWriter(file);
fAttributes.store(reader, "Hardware Configuration File");
reader.close();
fDirty = false;
if (fOriginal != null) {
// Copy the changes to the original
fOriginal.loadFromConfig(this);
}
}
}
private void load(File file) throws IOException {
FileReader reader = new FileReader(file);
fAttributes.load(reader);
reader.close();
}
/**
* Load the values of this Configuration from the given <code>TargetConfiguration</code>.
*
* @param prefs
* Source <code>TargetConfiguration</code>.
*/
private void loadFromConfig(TargetConfiguration config) {
fAttributes.clear();
for (Object obj : config.fAttributes.keySet()) {
String key = (String) obj;
setAttribute(key, config.getAttribute(key));
}
fDirty = config.fDirty;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfigurationWorkingCopy#setDefaults()
*/
public void restoreDefaults() {
// Set the defaults. If
for (String key : fDefaults.keySet()) {
String defvalue = fDefaults.get(key);
setAttribute(key, defvalue);
}
}
/**
* Put all default values into the default values map.
*/
private void initDefaults() {
fDefaults.put(ATTR_NAME, DEF_NAME);
fDefaults.put(ATTR_DESCRIPTION, DEF_DESCRIPTION);
fDefaults.put(ATTR_MCU, DEF_MCU);
fDefaults.put(ATTR_FCPU, Integer.toString(DEF_FCPU));
fDefaults.put(ATTR_PROGRAMMER_ID, DEF_PROGRAMMER_ID);
fDefaults.put(ATTR_HOSTINTERFACE, DEF_HOSTINTERFACE);
fDefaults.put(ATTR_PROGRAMMER_PORT, DEF_PROGRAMMER_PORT);
fDefaults.put(ATTR_PROGRAMMER_BAUD, DEF_PROGRAMMER_BAUD);
fDefaults.put(ATTR_BITBANGDELAY, DEF_BITBANGDELAY);
fDefaults.put(ATTR_PAR_EXITSPEC, DEF_PAR_EXITSPEC);
fDefaults.put(ATTR_USB_DELAY, DEF_USB_DELAY);
fDefaults.put(ATTR_JTAG_CLOCK, DEF_JTAG_CLOCK);
fDefaults.put(ATTR_DAISYCHAIN_ENABLE, DEF_DAISYCHAIN_ENABLE);
fDefaults.put(ATTR_DAISYCHAIN_UB, DEF_DAISYCHAIN_UB);
fDefaults.put(ATTR_DAISYCHAIN_UA, DEF_DAISYCHAIN_UA);
fDefaults.put(ATTR_DAISYCHAIN_BB, DEF_DAISYCHAIN_BB);
fDefaults.put(ATTR_DAISYCHAIN_BA, DEF_DAISYCHAIN_BA);
fDefaults.put(ATTR_PROGRAMMER_TOOL_ID, DEF_PROGRAMMER_TOOL_ID);
fDefaults.put(ATTR_GDBSERVER_ID, DEF_GDBSERVER_ID);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#isDebugCapable()
*/
public boolean isDebugCapable() {
// TODO Auto-generated method stub
return false;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#isImageLoaderCapable()
*/
public boolean isImageLoaderCapable() {
// TODO Auto-generated method stub
return false;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.ITargetConfiguration#dispose()
*/
public void dispose() {
fListeners.clear();
fIsDisposed = true;
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfiguration#addPropertyChangeListener(de.innot.
* avreclipse.core.targets.TargetConfiguration.ITargetConfigChangeListener)
*/
public void addPropertyChangeListener(ITargetConfigChangeListener listener) {
fListeners.add(listener);
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfiguration#removePropertyChangeListener(de.innot
* .avreclipse.core.targets.TargetConfiguration.ITargetConfigChangeListener)
*/
public void removePropertyChangeListener(ITargetConfigChangeListener listener) {
fListeners.remove(listener);
}
/**
* Informs all registered listeners that an attribute has changed.
*
* @param name
* the name of the changed attribute
* @param oldValue
* the old value, or <code>null</code> if not known or not relevant
* @param newValue
* the new value, or <code>null</code> if not known or not relevant
*/
protected void fireAttributeChangeEvent(String name, String oldValue, String newValue) {
if (name == null)
throw new IllegalArgumentException();
Object[] allListeners = fListeners.getListeners();
// Don't fire anything if there are no listeners
if (allListeners.length == 0) {
return;
}
for (Object changeListener : allListeners) {
ITargetConfigChangeListener listener = (ITargetConfigChangeListener) changeListener;
listener.attributeChange(TargetConfiguration.this, name, oldValue, newValue);
}
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.targets.ITargetConfiguration#validateAttribute(java.lang.String)
*/
public ValidationResult validateAttribute(String attr) {
// First check if one of the registered tools handles the attribute
ITargetConfigurationTool tool = fAttributeOwner.get(attr);
if (tool != null) {
return tool.validate(attr);
}
// The tools know nothing. Now go through all attributes that can be validated.
// But first Check if the attribute is actually know.
String value = fAttributes.getProperty(attr);
if (value == null) {
return new ValidationResult(Result.UNKNOWN_ATTRIBUTE, "");
}
if (ATTR_MCU.equals(attr)) {
return AVRHardwareConfigValidator.checkMCU(this);
} else if (ATTR_PROGRAMMER_ID.equals(attr)) {
return AVRHardwareConfigValidator.checkProgrammer(this);
} else if (ATTR_JTAG_CLOCK.equals(attr)) {
return AVRHardwareConfigValidator.checkJTAGClock(this);
} else if (ATTR_DAISYCHAIN_BB.equals(attr)) {
return AVRHardwareConfigValidator.checkJTAGDaisyChainBitsBefore(this);
} else if (ATTR_DAISYCHAIN_BA.equals(attr)) {
return AVRHardwareConfigValidator.checkJTAGDaisyChainBitsAfter(this);
} else if (ATTR_DAISYCHAIN_UB.equals(attr)) {
return AVRHardwareConfigValidator.checkJTAGDaisyChainUnitsBefore(this);
} else if (ATTR_DAISYCHAIN_UA.equals(attr)) {
return AVRHardwareConfigValidator.checkJTAGDaisyChainUnitsAfter(this);
}
// TODO Auto-generated method stub
return new ValidationResult(Result.OK, "");
}
}