/* StyledTagSetConverter.java created 2007-09-28
*
*/
package org.signalml.domain.tag;
import java.awt.Color;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.swing.KeyStroke;
import org.apache.log4j.Logger;
import org.signalml.app.model.signal.PagingParameterDescriptor;
import org.signalml.domain.montage.Montage;
import org.signalml.domain.signal.SignalChecksum;
import org.signalml.exception.SanityCheckException;
import org.signalml.plugin.export.signal.SignalSelectionType;
import org.signalml.plugin.export.signal.Tag;
import org.signalml.plugin.export.signal.TagStyle;
import org.signalml.plugin.export.signal.tagStyle.TagAttributeValue;
import org.signalml.plugin.export.signal.tagStyle.TagStyleAttributeDefinition;
import org.signalml.util.ColorConverter;
import org.signalml.util.FloatArrayConverter;
import org.signalml.util.KeyStrokeConverter;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.basic.BooleanConverter;
import com.thoughtworks.xstream.converters.basic.FloatConverter;
import com.thoughtworks.xstream.converters.basic.IntConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
/**
* This class is responsible for marshaling/unmarshaling {@link StyledTagSet}
* objects to/from textual data.
* Doesn't seem to be used.
*
* @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o.
*/
public class StyledTagSetConverter implements Converter {
protected static final Logger logger = Logger.getLogger(StyledTagSetConverter.class);
/**
* Holds the version number of the format of the tag file written by this converter to the XML files.
* It is written to the tag files by the marshal method. Unmarshall method throws an exception if
* the file tag document given to it has a different format version.
*/
private static final double formatVersion = 1.0;
/**
* Name of the section containing tags.
*/
public static final String TAG_NODE_NAME = "tags";
private FloatConverter floatConverter = new FloatConverter();
private IntConverter intConverter = new IntConverter();
private ColorConverter colorConverter = new ColorConverter();
private KeyStrokeConverter keyStrokeConverter = new KeyStrokeConverter();
private FloatArrayConverter floatArrayConverter = new FloatArrayConverter();
private BooleanConverter booleanConverter = new BooleanConverter("1", "0", false);
/**
* Converts a {@link StyledTagSet StyledTagSet} to textual data.
* @param value the object to be marshaled
* @param writer a stream to write to
* @param context a context that allows nested objects to be processed
* by XStream
*/
@Override
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
StyledTagSet sts = (StyledTagSet) value;
TagSignalIdentification ident = sts.getTagSignalIdentification();
String info = sts.getInfo();
String montageInfo = sts.getMontageInfo();
Montage montage = sts.getMontage();
Collection<TagStyle> styles;
writer.addAttribute("formatVersion", floatConverter.toString(formatVersion));
if (ident != null) {
writer.startNode("datafileIdentification");
if (ident.getFileName() != null) {
writer.startNode("name");
writer.setValue(ident.getFileName());
writer.endNode();
}
if (ident.getFormatId() != null) {
writer.startNode("format");
writer.addAttribute("id", ident.getFormatId());
writer.endNode();
}
SignalChecksum signalChecksum = ident.getChecksum();
if (signalChecksum != null) {
writer.startNode("signature");
writer.addAttribute("method", signalChecksum.getMethod());
writer.addAttribute("offset", intConverter.toString(signalChecksum.getOffset()));
writer.addAttribute("length", intConverter.toString(signalChecksum.getLength()));
writer.addAttribute("value", signalChecksum.getValue());
writer.endNode();
}
writer.endNode();
}
writer.startNode("paging");
writer.addAttribute("pageSize", floatConverter.toString(sts.getPageSize()));
writer.addAttribute("blocksPerPage", intConverter.toString(sts.getBlocksPerPage()));
writer.endNode();
writer.startNode("tagDefinitions");
if (sts.getName() != null) {
writer.startNode("name");
writer.setValue(sts.getName());
writer.endNode();
}
writer.startNode("defGroup");
writer.addAttribute("name", "pageTags");
styles = sts.getPageStyles();
for (TagStyle style : styles) {
marshalStyle(writer,style);
}
writer.endNode();
writer.startNode("defGroup");
writer.addAttribute("name", "blockTags");
styles = sts.getBlockStyles();
for (TagStyle style : styles) {
marshalStyle(writer,style);
}
writer.endNode();
writer.startNode("defGroup");
writer.addAttribute("name", "channelTags");
styles = sts.getChannelStyles();
for (TagStyle style : styles) {
marshalStyle(writer,style);
}
writer.endNode();
writer.endNode(); // tagDefinitions
writer.startNode("tagData");
if (info != null) {
writer.startNode("description");
writer.setValue(info);
writer.endNode();
}
if (montage != null) {
writer.startNode("montage");
context.convertAnother(montage);
writer.endNode();
}
else if (montageInfo != null && !montageInfo.isEmpty()) {
writer.startNode("montageInfo");
writer.setValue(montageInfo);
writer.endNode();
}
writer.startNode(TAG_NODE_NAME);
for (Tag tag : sts.getTags())
marshalTag(writer, tag);
writer.endNode();
writer.endNode(); // tagData
}
/**
* Converts a {@link Tag Tag} to textual data using the given writer.
* @param writer a stream to write to
* @param tag a tag to be converted to XML
*/
private void marshalTag(HierarchicalStreamWriter writer, Tag tag) {
writer.startNode("tag");
writer.addAttribute("type", tag.getStyle().getType().toString());
writer.addAttribute("name", tag.getStyle().getName());
writer.addAttribute("channelNumber", intConverter.toString(tag.getChannel()));
writer.addAttribute("position", floatConverter.toString(tag.getPosition()));
writer.addAttribute("length", floatConverter.toString(tag.getLength()));
marshalTagAttributes(writer, tag);
writer.endNode();
}
/**
* Converts tag's attributes to textual data using the given writer.
* @param writer a stream to write to
* @param tag a tag whose attributes will be be converted to XML
*/
private void marshalTagAttributes(HierarchicalStreamWriter writer, Tag tag) {
if (tag.getAnnotation() != null) {
writer.startNode("annotation");
writer.setValue(tag.getAnnotation());
writer.endNode();
}
for (TagAttributeValue attribute: tag.getAttributes().getAttributesList()) {
writer.startNode(attribute.getAttributeDefinition().getCode());
writer.setValue(attribute.getAttributeValue());
writer.endNode();
}
}
/**
* Converts a {@link TagStyle TagStyle} to textual data.
* @param style The TagStyle to be marshaled
* @param writer A stream to write to.
*/
private void marshalStyle(HierarchicalStreamWriter writer, TagStyle style) {
writer.startNode("tagItem");
writer.addAttribute("name", style.getName());
String description = style.getDescription();
writer.addAttribute("description", description != null ? description : "");
writer.addAttribute("fillColor", colorConverter.toString(style.getFillColor()));
writer.addAttribute("outlineColor", colorConverter.toString(style.getOutlineColor()));
writer.addAttribute("outlineWidth", floatConverter.toString(style.getOutlineWidth()));
writer.addAttribute("outlineDash", floatArrayConverter.toString(style.getOutlineDash()));
writer.addAttribute("keyShortcut", keyStrokeConverter.toString(style.getKeyStroke()));
writer.addAttribute("marker", booleanConverter.toString(style.isMarker()));
writer.addAttribute("visible", booleanConverter.toString(style.isVisible()));
if (style.getAttributesDefinitions().getSize() > 0) {
writer.startNode("attributes");
for (TagStyleAttributeDefinition definition:
style.getAttributesDefinitions().getAttributesDefinitionsList()) {
writer.startNode("attribute");
writer.addAttribute("code", definition.getCode());
writer.addAttribute("displayName", definition.getDisplayName());
writer.addAttribute("visible", definition.isVisible() ? "true" : "false");
writer.endNode();
}
writer.endNode();
}
writer.endNode();
}
/**
* Convert textual data back into a {@link StyledTagSet StyledTagSet}.
* @param reader the stream to read the text from.
* @param context a context that allows nested objects to be processed by XStream.
* @return the created StyledTagSet
*/
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
TagSignalIdentification ident = null;
String info = null;
String montageInfo = null;
String presetName = null;
TagStyle style;
Tag tag;
float position;
float length;
int channel;
String annotation;
TagStyles styles = new TagStyles();
TreeSet<Tag> tags = new TreeSet<Tag>();
float pageSize = PagingParameterDescriptor.DEFAULT_PAGE_SIZE;
int blocksPerPage = PagingParameterDescriptor.DEFAULT_BLOCKS_PER_PAGE;
boolean pagingOk = false;
Montage montage = null;
if (Double.parseDouble(reader.getAttribute("formatVersion")) != formatVersion) {
throw new SanityCheckException("Unsupported tag file format version. Svarog supports only tag file with format version equal to " + formatVersion + ".");
}
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("datafileIdentification".equals(reader.getNodeName())) {
ident = new TagSignalIdentification();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("name".equals(reader.getNodeName())) {
ident.setFileName(reader.getValue());
}
else if ("format".equals(reader.getNodeName())) {
ident.setFormatId(reader.getAttribute("id"));
}
else if ("signature".equals(reader.getNodeName())) {
SignalChecksum signalChecksum = new SignalChecksum();
signalChecksum.setMethod(reader.getAttribute("method"));
signalChecksum.setOffset((Integer) intConverter.fromString(reader.getAttribute("offset")));
signalChecksum.setLength((Integer) intConverter.fromString(reader.getAttribute("length")));
signalChecksum.setValue(reader.getAttribute("value"));
ident.setChecksum(signalChecksum);
}
reader.moveUp();
}
} else if ("paging".equals(reader.getNodeName())) {
boolean pageSizeOk = false;
boolean blocksPerPageOk = false;
String attr = reader.getAttribute("pageSize");
if (attr != null && !attr.isEmpty()) {
pageSize = (Float) floatConverter.fromString(attr);
pageSizeOk = true;
}
attr = reader.getAttribute("blocksPerPage");
if (attr != null && !attr.isEmpty()) {
blocksPerPage = (Integer) intConverter.fromString(attr);
blocksPerPageOk = true;
}
if (pageSizeOk && blocksPerPageOk) {
pagingOk = true;
}
} else if ("tagDefinitions".equals(reader.getNodeName())) {
while (reader.hasMoreChildren()) {
reader.moveDown();
String type = reader.getAttribute("name");
if (type == null) {
if ("name".equals(reader.getNodeName())) {
presetName = reader.getValue();
}
} else if ("pageTags".equals(type)) {
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("tagItem".equals(reader.getNodeName())) {
style = new TagStyle(SignalSelectionType.PAGE);
unmarshalStyle(reader, style);
styles.addStyle(style);
}
reader.moveUp();
}
} else if ("blockTags".equals(type)) {
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("tagItem".equals(reader.getNodeName())) {
style = new TagStyle(SignalSelectionType.BLOCK);
unmarshalStyle(reader, style);
styles.addStyle(style);
}
reader.moveUp();
}
} else if ("channelTags".equals(type)) {
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("tagItem".equals(reader.getNodeName())) {
style = new TagStyle(SignalSelectionType.CHANNEL);
unmarshalStyle(reader, style);
styles.addStyle(style);
}
reader.moveUp();
}
}
reader.moveUp();
}
} else if ("tagData".equals(reader.getNodeName())) {
/* paging information is needed before processing tag data to generate tag styles
* for tags for which styles were not generated in the file
*/
if (!pagingOk) {
logger.warn("WARNING: tag doesn't contain paging information or paging information is AFTER the tag information, assuming defaults");
pageSize = PagingParameterDescriptor.DEFAULT_PAGE_SIZE;
blocksPerPage = PagingParameterDescriptor.DEFAULT_BLOCKS_PER_PAGE;
}
TagStylesGenerator tagStylesGenerator = new TagStylesGenerator(pageSize, blocksPerPage);
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("description".equals(reader.getNodeName())) {
info = reader.getValue();
} else if ("montage".equals(reader.getNodeName())) {
montage = (Montage) context.convertAnother(null, Montage.class);
} else if ("montageInfo".equals(reader.getNodeName())) {
montageInfo = reader.getValue();
} else if (TAG_NODE_NAME.equals(reader.getNodeName())) {
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("tag".equals(reader.getNodeName())) {
String tagName = reader.getAttribute("name");
SignalSelectionType type = null;
if (reader.getAttribute("type") != null)
type = SignalSelectionType.valueOf(reader.getAttribute("type"));
channel = (Integer) intConverter.fromString(reader.getAttribute("channelNumber"));
position = (Float) floatConverter.fromString(reader.getAttribute("position"));
length = (Float) floatConverter.fromString(reader.getAttribute("length"));
style = styles.getStyle(type, tagName);
if (style == null) {
style = tagStylesGenerator.getSmartStyleFor(tagName, length, channel);
styles.addStyle(style);
}
annotation = null;
tag = new Tag(style,position,length,channel,annotation);
while (reader.hasMoreChildren()) {
reader.moveDown();
String attributeCode = reader.getNodeName();
if ("annotation".equals(attributeCode)) {
tag.setAnnotation(reader.getValue());
}
else {
tag.addAttributeToTag(attributeCode, reader.getValue());
}
reader.moveUp();
}
tags.add(tag);
}
reader.moveUp();
}
}
reader.moveUp();
}
}
reader.moveUp();
}
StyledTagSet sts = new StyledTagSet(styles,tags,pageSize,blocksPerPage);
sts.setTagSignalIdentification(ident);
sts.setInfo(info);
sts.setName(presetName);
if (montage != null) {
sts.setMontage(montage);
} else {
sts.setMontageInfo(montageInfo);
}
return sts;
}
/**
* Convert textual data back into a {@link TagStyle TagStyle}.
* @param reader the stream to read the text from.
* @param style the TagStyle object on which read data will be written
*/
private void unmarshalStyle(HierarchicalStreamReader reader, TagStyle style) {
String name = reader.getAttribute("name");
String description = reader.getAttribute("description");
Color fillColor = (Color) colorConverter.fromString(reader.getAttribute("fillColor"));
Color outlineColor = (Color) colorConverter.fromString(reader.getAttribute("outlineColor"));
float outlineWidth = (Float) floatConverter.fromString(reader.getAttribute("outlineWidth"));
float[] outlineDash = (float[]) floatArrayConverter.fromString(reader.getAttribute("outlineDash"));
KeyStroke keyStroke = (KeyStroke) keyStrokeConverter.fromString(reader.getAttribute("keyShortcut"));
boolean marker = false;
String markerAttr = reader.getAttribute("marker");
if (markerAttr != null && !markerAttr.isEmpty()) {
marker = ((Boolean) booleanConverter.fromString(markerAttr)).booleanValue();
}
boolean visible = true;
String visibleAttr = reader.getAttribute("visible");
if (visibleAttr != null && !visibleAttr.isEmpty()) {
visible = ((Boolean) booleanConverter.fromString(visibleAttr)).booleanValue();
}
if (name == null || fillColor == null || outlineColor == null) {
throw new NullPointerException("Bad tag file format");
}
if (reader.hasMoreChildren()) {
reader.moveDown();
while ("attributes".equals(reader.getNodeName()) && reader.hasMoreChildren()) {
reader.moveDown();
if ("attribute".equals(reader.getNodeName())) {
String code = reader.getAttribute("code");
String displayName = reader.getAttribute("displayName");
String visibleStr = reader.getAttribute("visible");
boolean attributeVisible = true;
if ("false".equals(visibleStr))
attributeVisible = false;
TagStyleAttributeDefinition attributeDefinition = new TagStyleAttributeDefinition(code, displayName, attributeVisible);
style.getAttributesDefinitions().addAttributeDefinition(attributeDefinition);
}
reader.moveUp();
}
reader.moveUp();
}
style.setParameters(name, description, fillColor, outlineColor, outlineWidth, outlineDash, keyStroke, marker, visible);
}
/**
* Determines whether this converter can marshal a particular type.
* @param clazz the Class representing the object type to be converted
* @return true if this converter can marshal a particular type,
* false otherwise
*/
@SuppressWarnings("unchecked")
@Override
public boolean canConvert(Class clazz) {
return (StyledTagSet.class.equals(clazz));
}
/**
* Static method that marshals given tag set and returns it as a string
* @param tags tags to marshal
* @return marshaled tag set
*/
public static String marshalTagsToString(SortedSet<Tag> tags) throws IOException {
StringWriter stringWriter = new StringWriter();
PrettyPrintWriter writer = new PrettyPrintWriter(stringWriter);
StyledTagSetConverter converter = new StyledTagSetConverter();
for (Tag tag : tags)
converter.marshalTag(writer, tag);
writer.close();
stringWriter.close();
return stringWriter.toString();
}
}