/* AbstractReaderSignal.java created 2007-11-22
*
*/
package org.signalml.app.document.signal;
import static org.signalml.app.util.i18n.SvarogI18n._;
import java.beans.IntrospectionException;
import java.io.InvalidClassException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.signalml.app.document.TagDocument;
import org.signalml.app.model.components.LabelledPropertyDescriptor;
import org.signalml.app.model.components.PropertyProvider;
import org.signalml.app.model.document.opensignal.elements.SignalParameters;
import org.signalml.app.view.signal.SampleSourceUtils;
import org.signalml.app.view.signal.SignalPlot;
import org.signalml.app.view.signal.SignalView;
import org.signalml.domain.montage.Montage;
import org.signalml.domain.montage.SignalConfigurer;
import org.signalml.domain.signal.samplesource.OriginalMultichannelSampleSource;
import org.signalml.exception.SanityCheckException;
import org.signalml.plugin.export.SignalMLException;
import org.signalml.plugin.export.signal.AbstractDocument;
import org.signalml.plugin.export.signal.ExportedTagDocument;
import org.springframework.context.MessageSourceResolvable;
/**
* Abstract implementation of a {@link SignalDocument}.
* Only implements the methods of {@code SignalDocument}.
* Stores the information about:
* <ul>
* <li>all {@link TagDocument tag documents} and the active one</li>
* <li>the size of a block and a page</li>
* <li>the {@link OriginalMultichannelSampleSource source} of unprocessed (raw)
* samples</li>
* <li>the main {@link Montage montage} for the signal</li>
* </ul>
*
* @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o.
*/
public abstract class AbstractSignal extends AbstractDocument implements SignalDocument, MessageSourceResolvable, PropertyProvider {
protected static final Logger logger = Logger.getLogger(AbstractSignal.class);
/**
* the source of unprocessed (raw) samples
*/
protected OriginalMultichannelSampleSource sampleSource = null;
/**
* the currently active (selected) {@link TagDocument tag document}
*/
protected TagDocument activeTag;
/**
* list of all {@link TagDocument tag documents} for this signal
*/
protected List<TagDocument> tagDocuments = new LinkedList<TagDocument>();
/**
* the length of the page in seconds
*/
protected float pageSize = SignalParameters.DEFAULT_PAGE_SIZE;
/**
* the number of blocks in a page
*/
protected int blocksPerPage = SignalParameters.DEFAULT_BLOCKS_PER_PAGE;
/**
* the length of a block in seconds
*/
protected float blockSize = pageSize / blocksPerPage;
/**
* the main {@link Montage montage} for this signal
*/
protected Montage montage = null;
/**
* the number of {@link TagDocument tag documents} for this signal
* that have no backing file {@code + 1}
*/
private int namelessTagCounter = 1;
/**
* Constructor.
*/
public AbstractSignal() {
}
@Override
public void closeDocument() throws SignalMLException {
sampleSource = null;
super.closeDocument();
}
@Override
public float getSamplingFrequency() {
return sampleSource.getSamplingFrequency();
}
@Override
public int getChannelCount() {
return sampleSource.getChannelCount();
}
@Override
public float getMinSignalLength() {
return ((float) SampleSourceUtils.getMinSampleCount(sampleSource)) / getSamplingFrequency();
}
@Override
public float getMaxSignalLength() {
return ((float) SampleSourceUtils.getMaxSampleCount(sampleSource)) / getSamplingFrequency();
}
@Override
public int getPageCount() {
return (int) Math.ceil(getMaxSignalLength() / getPageSize());
}
@Override
public int getBlockCount() {
return (int) Math.ceil(getMaxSignalLength() / getBlockSize());
}
@Override
public OriginalMultichannelSampleSource getSampleSource() {
return sampleSource;
}
@Override
public String[] getCodes() {
return new String[] { "signalDocument" };
}
@Override
public Object[] getArguments() {
return new Object[] {
getName()
};
}
@Override
@Deprecated
public String getDefaultMessage() {
// XXX: remove this method and call getName() directly
return getName();
}
@Override
public float getPageSize() {
if (activeTag != null) {
return activeTag.getPageSize();
}
return pageSize;
}
@Override
public void setPageSize(float pageSize) {
if (this.pageSize != pageSize) {
float last = this.pageSize;
this.pageSize = pageSize;
this.blockSize = pageSize / blocksPerPage;
pcSupport.firePropertyChange(PAGE_SIZE_PROPERTY, last, pageSize);
}
}
@Override
public int getBlocksPerPage() {
if (activeTag != null) {
return activeTag.getBlocksPerPage();
}
return blocksPerPage;
}
@Override
public void setBlocksPerPage(int blocksPerPage) {
if (this.blocksPerPage != blocksPerPage) {
int last = this.blocksPerPage;
this.blocksPerPage = blocksPerPage;
this.blockSize = pageSize / blocksPerPage;
pcSupport.firePropertyChange(BLOCKS_PER_PAGE_PROPERTY, last, blocksPerPage);
}
}
@Override
public float getBlockSize() {
if (activeTag != null) {
return activeTag.getBlockSize();
}
return blockSize;
}
@Override
public List<TagDocument> getTagDocuments() {
return tagDocuments;
}
@Override
public void addTagDocument(TagDocument document) {
if (tagDocuments.contains(document)) {
return;
}
if (document.getBackingFile() == null) {
document.setFallbackName(Integer.toString(namelessTagCounter));
namelessTagCounter++;
}
addDependentDocument(document);
tagDocuments.add(document);
pcSupport.fireIndexedPropertyChange(TAG_DOCUMENTS_PROPERTY, tagDocuments.indexOf(document), null, document);
if (activeTag == null && tagDocuments.size() == 1) {
setActiveTag(document);
}
}
@Override
public void removeTagDocument(TagDocument document) {
int index = tagDocuments.indexOf(document);
if (index < 0) {
return;
}
tagDocuments.remove(index);
if (document == activeTag) {
if (tagDocuments.size() > 0) {
setActiveTag((TagDocument) tagDocuments.get(0));
} else {
setActiveTag(null);
}
}
removeDependentDocument(document);
pcSupport.fireIndexedPropertyChange(TAG_DOCUMENTS_PROPERTY, index, document, null);
}
@Override
public TagDocument getActiveTag() {
return activeTag;
}
@Override
public void setActiveTag(TagDocument document) {
if (activeTag != document) {
if (document != null) {
if (!getDependentDocuments().contains(document)) {
throw new SanityCheckException("Tag set to be active not dependent");
}
}
TagDocument oldActiveTag = activeTag;
activeTag = document;
pcSupport.firePropertyChange(ACTIVE_TAG_PROPERTY, oldActiveTag, activeTag);
}
}
@Override
public Montage getMontage() {
if (getSampleSource() == null) {
return null;
}
if (montage == null) {
montage = createDefaultMontage();
}
return montage;
}
@Override
public boolean isMontageCreated() {
if (montage == null)
return false;
return true;
}
protected Montage createDefaultMontage() {
return SignalConfigurer.createMontage(this);
}
protected Montage createDefaultMontage(int numberOfChannels) {
return SignalConfigurer.createMontage(numberOfChannels);
}
@Override
public void setMontage(Montage montage) {
if (this.montage != montage) {
Montage oldMontage = this.montage;
this.montage = montage;
pcSupport.firePropertyChange(MONTAGE_PROPERTY, oldMontage, montage);
}
}
@Override
public String getMontageInfo() {
StringBuilder sb = new StringBuilder();
String description = getMontage().getDescription();
if (description != null) {
sb.append(description);
}
SignalView view = (SignalView) getDocumentView();
if (view != null) {
LinkedList<SignalPlot> plots = view.getPlots();
Iterator<SignalPlot> it = plots.iterator();
SignalPlot plot;
Montage montage;
it.next(); // skip master
int cnt = 1;
while (it.hasNext()) {
plot = it.next();
montage = plot.getLocalMontage();
if (montage != null) {
description = montage.getDescription();
if (description != null) {
sb.append("\n\nClone ").append(cnt).append("\n------------\n");
sb.append(description);
}
}
cnt++;
}
}
return sb.toString();
}
@Override
public List<ExportedTagDocument> getExportedTagDocuments() {
List<ExportedTagDocument> exportedTagDocuments = new LinkedList<ExportedTagDocument>();
List<TagDocument> tagDocuments = getTagDocuments();
for (TagDocument tagDocument : tagDocuments)
exportedTagDocuments.add(tagDocument);
return exportedTagDocuments;
}
@Override
public List<LabelledPropertyDescriptor> getPropertyList() throws IntrospectionException {
LinkedList<LabelledPropertyDescriptor> list = new LinkedList<LabelledPropertyDescriptor>();
list.add(new LabelledPropertyDescriptor(_("sampling frequency"), "samplingFrequency", SignalMLDocument.class));
list.add(new LabelledPropertyDescriptor(_("channel count"), "channelCount", SignalMLDocument.class));
list.add(new LabelledPropertyDescriptor(_("page size"), "pageSize", SignalMLDocument.class));
list.add(new LabelledPropertyDescriptor(_("blocks per page"), "blocksPerPage", SignalMLDocument.class));
list.add(new LabelledPropertyDescriptor(_("block size"), "blockSize", SignalMLDocument.class, "getBlockSize", null));
list.add(new LabelledPropertyDescriptor(_("minimum signal length"), "minSignalLength", SignalMLDocument.class, "getMinSignalLength", null));
list.add(new LabelledPropertyDescriptor(_("maximum signal length"), "maxSignalLength", SignalMLDocument.class, "getMaxSignalLength", null));
list.add(new LabelledPropertyDescriptor(_("page count"), "pageCount", SignalMLDocument.class, "getPageCount", null));
list.add(new LabelledPropertyDescriptor(_("block count"), "blockCount", SignalMLDocument.class, "getBlockCount", null));
return list;
}
@Override
public ArrayList<String> getMontageChannelLabels() {
Montage montage = getMontage();
ArrayList<String> labels = new ArrayList<String>(montage.getMontageChannelCount());
for (int i = 0; i < montage.getMontageChannelCount(); ++i) {
labels.add(montage.getMontageChannelLabelAt(i));
}
return labels;
}
@Override
public ArrayList<String> getSourceChannelLabels() {
Montage montage = getMontage();
ArrayList<String> labels = new ArrayList<String>(montage.getSourceChannelCount());
for (int i = 0; i < montage.getSourceChannelCount(); ++i) {
labels.add(montage.getSourceChannelLabelAt(i));
}
return labels;
}
@Override
public SignalView getSignalView() throws InvalidClassException {
if (!(getDocumentView() instanceof SignalView)) throw new InvalidClassException("document view for a signal document must be always of type SignalView");
return (SignalView) getDocumentView();
}
}