/*******************************************************************************
* 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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Description for a single Fuse or Lockbits byte.
* <p>
* </p>
*
* @author Thomas Holland
* @since 2.3
* @version 2.4 Added description
*
*/
public class ByteDescription implements IFuseObjectDescription {
protected final static String ATTR_BYTE_INDEX = "index";
protected final static String ATTR_DESCRIPTION = "description";
protected final static String ATTR_BYTE_NAME = "name";
protected final static String ATTR_DEFAULT_VALUE = "default";
protected final static String ATTR_SIZE = "size";
/** The type of this byte. Either FUSE or LOCKBITS */
private final FuseType fType;
// TODO: Do we need this?
/** Fuse byte name (from the part description file). */
private final String fName;
/** A short description of this Byte. */
private final String fDescription;
/** The index of the Byte. (0 up to 5 for fuse bytes, 0 for the lockbits byte) */
private final int fIndex;
/**
* The number of bytes in this object.
*
* @since 2.4
*/
private final int fSize;
/** The default values. <code>-1</code> if no default value is defined. */
private final int fDefaultValue;
/** List with all BitFieldDescriptions for this byte. */
private final List<BitFieldDescription> fBitFieldList;
/**
* Create a new ByteDescription for the byte with the given number parameters.
*
* @param type
* Either {@link FuseType#FUSE} or {@link FuseType#LOCKBITS}
* @param name
* The name of this byte as defined in the part description file, e.g "LOW",
* "FUSEBYTE3" or "LOCKBITS".
* @param index
* The index of this byte within its memory. <code>0</code> up to <code>5</code> for
* fuse bytes (depending on MCU) or <code>0</code> for the lockbits byte.
*/
public ByteDescription(FuseType type, String description, String name, int index, int size,
int defaultvalue) {
fType = type;
fDescription = description;
fName = name;
fIndex = index;
fSize = size;
fDefaultValue = defaultvalue;
fBitFieldList = new ArrayList<BitFieldDescription>();
}
/**
* Construct a new ByteDescription from a XML <fusebyte> or <lockbitsbyte> node.
* <p>
* This constructor will take the node and parse the values from the "index", "name" and
* "default" attributes.
* </p>
* <p>
* Then the list of {@link BitFieldDescription}s is filled from all <bitfield> elements.
* </p>
*
* @param bitfieldnode
* A <bitfield> document node.
*/
public ByteDescription(Node byteelement) throws IllegalArgumentException {
// Default / Error check values
fBitFieldList = new ArrayList<BitFieldDescription>();
// Get the type of this byte from the given node.
if (byteelement.getNodeName().equalsIgnoreCase(FuseType.FUSE.getElementName())) {
fType = FuseType.FUSE;
} else {
fType = FuseType.LOCKBITS;
}
// The byte element has up to five attributes: "index" and "name" are required
// "default", "description" and "size" are optional, depending on the version
// of the generator.
NamedNodeMap attrs = byteelement.getAttributes();
Node indexnode = attrs.getNamedItem(ATTR_BYTE_INDEX);
if (indexnode == null) {
throw new IllegalArgumentException("Required attribute \"" + ATTR_BYTE_INDEX
+ "\" for element <" + fType.getElementName() + "> missing.");
}
fIndex = Integer.decode(indexnode.getTextContent());
Node namenode = attrs.getNamedItem(ATTR_BYTE_NAME);
if (namenode == null) {
throw new IllegalArgumentException("Required attribute \"" + ATTR_BYTE_NAME
+ "\" for element <" + fType.getElementName() + "> missing.");
}
fName = namenode.getNodeValue();
Node sizenode = attrs.getNamedItem(ATTR_SIZE);
if (sizenode != null) {
fSize = Integer.decode(sizenode.getNodeValue());
} else {
fSize = 1;
}
Node descriptionNode = attrs.getNamedItem(ATTR_DESCRIPTION);
if (descriptionNode != null) {
fDescription = descriptionNode.getNodeValue();
} else {
fDescription = fName;
}
Node defaultnode = attrs.getNamedItem(ATTR_DEFAULT_VALUE);
if (defaultnode == null) {
fDefaultValue = -1;
} else {
fDefaultValue = Integer.decode(defaultnode.getNodeValue());
}
// Now read the children of the byte element:
// one default element (optional) and at least one <bitfield>
NodeList children = byteelement.getChildNodes();
for (int n = 0; n < children.getLength(); n++) {
Node child = children.item(n);
if (BitFieldDescription.TAG_BITFIELD.equalsIgnoreCase(child.getNodeName())) {
// get the BitfieldDescription Object and add it to the list
BitFieldDescription bfd = new BitFieldDescription(child);
fBitFieldList.add(bfd);
}
}
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.fuses.IByteDescription#getBitFieldDescriptions()
*/
public List<BitFieldDescription> getBitFieldDescriptions() {
return new ArrayList<BitFieldDescription>(fBitFieldList);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.fuses.IByteDescription#getName()
*/
public String getName() {
return fName;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.fuses.IByteDescription#getDescription()
*/
public String getDescription() {
return fDescription;
}
/* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.fuses.IFuseObjectDescription#getSize()
*/
public int getSize() {
return fSize;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.fuses.IByteDescription#getIndex()
*/
public int getIndex() {
return fIndex;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.fuses.IByteDescription#getDefaultValue()
*/
public int getDefaultValue() {
if (fType == FuseType.LOCKBITS) {
return 0xff;
}
return fDefaultValue;
}
/**
* Add a <code>BitFieldDescription</code> object to this ByteDescription.
* <p>
* The list of all BitFieldDescriptions can be retrieved with {@link #getBitFieldDescriptions()}
* .
* </p>
*
* @param bitfielddescription
* A single <code>BitFieldDescription</code>
*/
public void addBitFieldDescription(BitFieldDescription bitfielddescription) {
fBitFieldList.add(bitfielddescription);
}
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.toolinfo.fuses.IByteDescription#isCompatibleWith(de.innot.avreclipse
* .core.toolinfo.fuses.IByteDescription)
*/
public boolean isCompatibleWith(IFuseObjectDescription target) {
// Get the list of the target BitFieldDescriptions and convert it into a map with BitField
// names and their masks.
List<BitFieldDescription> targetlist = target.getBitFieldDescriptions();
Map<String, Integer> targetmap = new HashMap<String, Integer>();
for (BitFieldDescription bfd : targetlist) {
targetmap.put(bfd.getName(), bfd.getMask());
}
// now we can compare them with our bitfields.
for (BitFieldDescription ourbitfield : fBitFieldList) {
String name = ourbitfield.getName();
Integer mask = targetmap.get(name);
if (mask == null || mask != ourbitfield.getMask()) {
return false;
}
}
// All bitfields match -> success
return true;
}
/**
* Convert this ByteDescription Object to XML.
*
* @see MCUDescription#toXML(Document) for the DTD of the generated xml
*
* @param parentelement
* A <fusedescription> element node to which this description is added
*/
public void toXML(Node parentnode) {
Document document = parentnode.getOwnerDocument();
// Create the byte node
Element bytenode = document.createElement(fType.getElementName());
bytenode.setAttribute(ATTR_DESCRIPTION, fDescription);
bytenode.setAttribute(ATTR_BYTE_INDEX, Integer.toString(fIndex));
bytenode.setAttribute(ATTR_BYTE_NAME, fName);
bytenode.setAttribute(ATTR_SIZE, Integer.toString(fSize));
if (fDefaultValue != -1) {
bytenode.setAttribute(ATTR_DEFAULT_VALUE, toHex(fDefaultValue));
}
// add the bitfield description elements
for (BitFieldDescription bfd : fBitFieldList) {
bfd.toXML(bytenode);
}
parentnode.appendChild(bytenode);
}
/**
* Format the given integer to a String with the format "0xXX".
* <p>
* Unlike the normal <code>Integer.toHexString(i)</code> method, this method will always produce
* two digits, even with the high nibble at zero, and will output the hex value in uppercase.
* This should make the value more readable than the standard <code>Integer.toHexString</code>
* output.
* </p>
*
* @param value
* Single byte value
* @return String with the byte value as "0xXX"
*/
public static String toHex(int value) {
String hex = "00" + Integer.toHexString(value);
return "0x" + hex.substring(hex.length() - 2).toUpperCase();
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(fType.getElementName());
sb.append(" [");
sb.append(" default=" + fDefaultValue);
for (BitFieldDescription bfd : fBitFieldList) {
sb.append(", " + bfd.toString());
}
sb.append("]");
return sb.toString();
}
}