package org.signalml.domain.montage.generators;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.signalml.domain.montage.system.MontageGenerators;
/**
* A {@link Converter} for unmarshalling the list of {@link IMontageGenerator
* montage generators} definitions from XML.
*
* @author Piotr Szachewicz
*/
public class MontageGeneratorsConverter implements Converter {
/**
* Montage generators that are added to all EEG systems by default.
*/
private static final List<IMontageGenerator> defaultMontageGenerators = new ArrayList<IMontageGenerator>()
{{
add(new RawMontageGenerator());
add(new CommonAverageMontageGenerator());
add(new LeftEarMontageGenerator());
add(new RightEarMontageGenerator());
add(new LinkedEarsMontageGenerator());
}
};
/**
* Logger for recording the history of execution.
*/
protected static final Logger logger = Logger.getLogger(MontageGeneratorsConverter.class);
@Override
public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext mc) {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Adds the default montage generators to the list passed as an argument.
* @param montageGenerators the list to which default montage generators
* will be added.
*/
public static void addDefaultMontageGenerators(List<IMontageGenerator> montageGenerators) {
montageGenerators.addAll(defaultMontageGenerators);
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext uc) {
List<IMontageGenerator> montageGenerators = new ArrayList<IMontageGenerator>();
addDefaultMontageGenerators(montageGenerators);
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("montageGenerator".equals(reader.getNodeName())) {
IMontageGenerator montageGenerator = unmarshallMontageGenerator(reader);
if (montageGenerator != null) {
montageGenerators.add(montageGenerator);
}
}
reader.moveUp();
}
return montageGenerators;
}
/**
* Unmarshalls a {@link IMontageGenerator} from XML.
* @param reader the reader reading the current XML stream
* @return the montage generator that has been read (null if an error
* occured)
*/
private IMontageGenerator unmarshallMontageGenerator(HierarchicalStreamReader reader) {
String montageGeneratorName = "";
GeneratorType generatorType = null;
IMontageGenerator montageGenerator = null;
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("name".equals(reader.getNodeName())) {
montageGeneratorName = reader.getValue();
} else if ("type".equals(reader.getNodeName())) {
//generator type must be specified before the channels
//because the converter differently parses the channels
//depending on which type is specified.
try {
generatorType = GeneratorType.valueOf(reader.getValue());
} catch (IllegalArgumentException ex) {
logger.error("No such generator type: " + reader.getValue() + ". Montage generator will be omitted.");
generatorType = null;
montageGenerator = null;
}
} else if ("channels".equals(reader.getNodeName()) && generatorType != null) {
switch (generatorType) {
case SINGLE_REFERENCE:
String singleReferenceChannel = unmarshallSingleChannel(reader);
montageGenerator = new SingleReferenceMontageGenerator(singleReferenceChannel);
break;
case BIPOLAR_REFERENCE:
String[][] bipolarReferenceChannels = unmarshallPairsOfChannels(reader);
montageGenerator = new BipolarReferenceMontageGenerator(bipolarReferenceChannels);
break;
case AVERAGE_REFERENCE:
String[] averageReferenceChannels = unmarshallVectorOfChannels(reader);
montageGenerator = new AverageReferenceMontageGenerator(averageReferenceChannels);
break;
}
if (montageGenerator != null) {
montageGenerator.setName(montageGeneratorName);
}
}
reader.moveUp();
}
return montageGenerator;
}
/**
* Unmarshalls the single channel (used for {@link SingleReferenceMontageGenerator}.
* @param reader the reader reading the current XML stream
* @return the reference channel name
*/
private String unmarshallSingleChannel(HierarchicalStreamReader reader) {
reader.moveDown();
String channelName = null;
if ("channel".equals(reader.getNodeName())) {
channelName = reader.getValue();
}
reader.moveUp();
return channelName;
}
/**
* Unmarshalls a vector of channel names from XML file.
* @param reader the reader reading the current XML stream
* @return the vector of channels
*/
private String[] unmarshallVectorOfChannels(HierarchicalStreamReader reader) {
List<String> channels = new ArrayList<String>();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("channel".equals(reader.getNodeName())) {
channels.add(reader.getValue());
}
reader.moveUp();
}
String[] result = channels.toArray(new String[channels.size()]);
return result;
}
/**
* Unmarshalls pairs of channel labels using the XML reader.
* @param reader the reader reading the current XML stream
* @return the pairs of channel labels.
*/
private String[][] unmarshallPairsOfChannels(HierarchicalStreamReader reader) {
List<String[]> channels = new ArrayList<String[]>();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("pair".equals(reader.getNodeName())) {
int i = 0;
String[] pair = new String[2];
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("channel".equals(reader.getNodeName())) {
pair[i] = reader.getValue();
i++;
}
reader.moveUp();
}
channels.add(pair);
}
reader.moveUp();
}
String[][] result = new String[channels.size()][2];
int i = 0;
for (String[] pair : channels) {
result[i][0] = pair[0];
result[i][1] = pair[1];
i++;
}
return result;
}
@Override
public boolean canConvert(Class clazz) {
return (ArrayList.class.equals(clazz));
}
}