/******************************************************************************* * 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.toolinfo.fuses; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.osgi.framework.Bundle; import org.w3c.dom.Document; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import de.innot.avreclipse.AVRPlugin; import de.innot.avreclipse.core.IMCUProvider; /** * This class handles the list of Fuse and Lockbits descriptions. * <p> * Most AVR MCUs have between one and three fusebytes with the new ATXmega series having six. All * current AVR MCUs have also one Lockbits byte, which is very similar to the fuse bytes.<br> * The description of these fuses and lockbits for each MCU type is stored in a * {@link IMCUDescription} object. This class manages the list of all available fuse byte * descriptions. * </p> * <p> * To get the <code>IMCUDescription</code for a MCU id use * {@link #getDescription(String)}. * </p> * <p> * This class manages two lists of fuse description files. * The default list is included with the plugin and can be * found in the <code>properties/fusedesc/</code> folder * of this Plugin. * </p> * <p> * The second list is the for the current Eclipse instance * and is located in the instance state area * (<code>.metadata/.plugins/de.innot.avreclipse.core/fusesdesc/</code>) * </p> * <p> * Each folder contains serialized <code>MCUDescription</code> * objects as xml files. This class also has a cache of all descriptions already * requested to reduce disk access. * </p> * @author Thomas Holland * @since 2.2 * */ public class Fuses implements IMCUProvider { // paths to the default and instance properties files private final static String DEFAULTFOLDER = "/properties/fusedesc"; private final static String INSTANCEFOLDER = "fusedesc"; /** File name extension for <code>IMCUDescription</code> objects. */ private final static String DESCRIPTION_EXTENSION = ".desc"; /** Cache of accessed <code>IMCUDescription</code> objects */ private final Map<String, IMCUDescription> fCache; /** List of all MCU id values for which Descriptions exist */ private Set<String> fMCUList = null; private static Fuses fInstance = null; /** * Get the default instance of the Fuses class */ public static Fuses getDefault() { if (fInstance == null) fInstance = new Fuses(); return fInstance; } // protected constructor to prevent outside instantiation. protected Fuses() { // Init the cache fCache = new HashMap<String, IMCUDescription>(); } /** * Get the {@link IMCUDescription} with the fuse and lockbits descriptions for the given MCU id. * * @param mcuid * <code>String</code> with a valid MCU id * @return <code>IMCUDescription</code> for the MCU or <code>null</code> if the given MCU id * is unknown. * @throws IOException * if either the storage locations can't be accessed or a file exists, but can't be * accessed. */ public IMCUDescription getDescription(String mcuid) throws IOException { if (mcuid == null || (mcuid.length() == 0)) { return null; } // Check the cache first if (fCache.containsKey(mcuid)) { return fCache.get(mcuid); } // Look in the instance location first, // then in the defaults location IPath instancelocation = getInstanceStorageLocation(); IPath defaultlocation = null; try { defaultlocation = getDefaultStorageLocation(); } catch (IOException e1) { // This is rather unlikely and will only occur, if I forgot to copy // the build-in settings to the plugin. But just in case we continue // with something reasonable: The Instance location defaultlocation = getInstanceStorageLocation(); } IPath[] allpaths = new IPath[] { instancelocation, defaultlocation }; IMCUDescription desc = null; for (IPath path : allpaths) { desc = getDescriptionFromLocation(mcuid, path); if (desc != null) { break; } } // If a description was found enter it to the cache if (desc != null) { fCache.put(mcuid, desc); } return desc; // will still be null if nothing was found } /** * Return the number of Fuse Bytes for the given MCU. * <p> * This is a convenience method (almost) equivalent to * * <pre> * getDescription(mcuid).getFuseByteCount(); * </pre> * * </p> * * @param mcuid * A valid MCU id value. * @return <code>int</code> with the number of fuse bytes for the mcu or 0 if the MCU id is * unknown. * @throws IOException */ public int getFuseByteCount(String mcuid) throws IOException { // Get the Description IMCUDescription desc; desc = getDescription(mcuid); if (desc != null) { return desc.getByteCount(FuseType.FUSE); } return 0; } /** * Return the number of lockbits Bytes for the given MCU. * <p> * This is a convenience method (almost) equivalent to * * <pre> * getDescription(mcuid).getLockbitsByteCount(); * </pre> * * </p> * * @param mcuid * A valid MCU id value. * @return <code>int</code> with the number of fuse bytes for the mcu or 0 if the MCU id is * unknown. * @throws IOException */ public int getLockbitsByteCount(String mcuid) throws IOException { // Get the Description IMCUDescription desc; desc = getDescription(mcuid); if (desc != null) { return desc.getByteCount(FuseType.LOCKBITS); } return 0; } // // Methods of the IMCUProvider Interface // /* * (non-Javadoc) * * @see de.innot.avreclipse.core.IMCUProvider#getMCUInfo(java.lang.String) */ public String getMCUInfo(String mcuid) { int count = 0; try { count = getFuseByteCount(mcuid); } catch (IOException e) { return null; } return Integer.toString(count); } /* * (non-Javadoc) * * @see de.innot.avreclipse.core.IMCUProvider#getMCUList() */ public Set<String> getMCUList() { if (fMCUList != null) { return fMCUList; } fMCUList = new HashSet<String>(); // Look in the defaults location first, // then add the descriptions from the instance location IPath defaultlocation = null; try { defaultlocation = getDefaultStorageLocation(); } catch (IOException e1) { // This is rather unlikely and will only occur, if I forgot to copy // the build-in settings to the plugin. But just in case we continue // with something reasonable: The Instance location defaultlocation = getInstanceStorageLocation(); } IPath instancelocation = getInstanceStorageLocation(); IPath[] allpaths = new IPath[] { defaultlocation, instancelocation }; for (IPath path : allpaths) { File currfolder = path.toFile(); String[] allfiles = currfolder.list(new FilenameFilter() { // Little filter to accept only files ending with ".desc" public boolean accept(File dir, String name) { if (name.endsWith(DESCRIPTION_EXTENSION)) { return true; } return false; } }); // Iterate over all returned filenames, strip the extension and add // it to the set. if (allfiles != null) { for (String filename : allfiles) { String mcuid = filename.substring(0, filename.lastIndexOf('.')); fMCUList.add(mcuid); } } } // Return a copy of the List return new HashSet<String>(fMCUList); } /* * (non-Javadoc) * * @see de.innot.avreclipse.core.IMCUProvider#hasMCU(java.lang.String) */ public boolean hasMCU(String mcuid) { if (fMCUList == null) { getMCUList(); } return fMCUList.contains(mcuid); } /** * De-Serialize the DescriptionHolder object for a MCU from the given location. * * @param mcuid * <code>String</code> with the MCU id value. * @param location * <code>IPath</code> to a folder containing the serialized description holder * objects. * @throws IOException * when the description file is not readable or contains errors. * @return */ private IMCUDescription getDescriptionFromLocation(String mcuid, IPath location) throws IOException { String filename = mcuid + DESCRIPTION_EXTENSION; // Test if a file with the right name is in the location File file = location.append(filename).toFile(); if (!file.canRead()) { return null; } // OK, file exist. De-serialize it DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(file); MCUDescription fd = new MCUDescription(document); return fd; } catch (SAXParseException spe) { // TODO: rewrite the error handling code // Error generated by the parser System.out.println("\n** Parsing error" + ", line " + spe.getLineNumber() + ", uri " + spe.getSystemId()); System.out.println(" " + spe.getMessage()); // Use the contained exception, if any Exception x = spe; if (spe.getException() != null) x = spe.getException(); x.printStackTrace(); } catch (SAXException sxe) { // Error generated by this application // (or a parser-initialization error) Exception x = sxe; if (sxe.getException() != null) x = sxe.getException(); x.printStackTrace(); } catch (ParserConfigurationException pce) { // Parser with specified options can't be built pce.printStackTrace(); } // we end up here if an Exception was thrown. return null; } /** * Get the folder for the instance fuse description files. * <p> * The default location is the <code>fusedesc</code> folder in the core plugin storage area (<code>Worspace_loc/.metadata/plugins/de.innot.avreclipse.core/fusedesc</code>). * </p> * * @return <code>IPath</code> to the instance storage area. */ public IPath getInstanceStorageLocation() { IPath statelocation = AVRPlugin.getDefault().getStateLocation(); IPath location = statelocation.append(INSTANCEFOLDER); return location; } /** * Get the folder for the build-in fuse description files. * <p> * The default location is the <code>properties/fusedesc</code> folder in the core plugin. * </p> * * @return <code>IPath</code> to the default location. * @throws IOException */ public IPath getDefaultStorageLocation() throws IOException { Bundle avrplugin = AVRPlugin.getDefault().getBundle(); URL locationurl = avrplugin.getEntry(DEFAULTFOLDER); IPath location = new Path(FileLocator.toFileURL(locationurl).getPath()); return location; } }