/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.sequence; import icy.file.FileUtil; import icy.file.xml.XMLPersistent; import icy.image.lut.LUT; import icy.painter.Overlay; import icy.roi.ROI; import icy.system.IcyExceptionHandler; import icy.util.StringUtil; import icy.util.XMLUtil; import java.util.LinkedList; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * @author Stephane */ public class SequencePersistent implements XMLPersistent { private final static String ID_META = "meta"; private final static String ID_ROIS = "rois"; private final static String ID_OVERLAYS = "overlays"; private final static String ID_LUT = "lut"; private final Sequence sequence; private Document document; /** * */ public SequencePersistent(Sequence sequence) { super(); this.sequence = sequence; document = XMLUtil.createDocument(true); } /** * Should return <code>null</code> if Sequence is not identified (no file name) */ private String getXMLFileName() { final String baseName = sequence.getOutputBaseName("meta"); if (StringUtil.isEmpty(baseName)) return null; return baseName + sequence.getOutputExtension() + XMLUtil.FILE_DOT_EXTENSION; } /** * Load XML persistent data.<br> * Return true if XML data has been correctly loaded. */ public boolean loadXMLData() { final String xmlFilename = getXMLFileName(); boolean result; Exception exc = null; if ((xmlFilename != null) && FileUtil.exists(xmlFilename)) { try { // load xml file into document document = XMLUtil.loadDocument(xmlFilename, true); // load data from XML document if (document != null) result = loadFromXML(getRootNode()); else { document = XMLUtil.createDocument(true); result = false; } } catch (Exception e) { exc = e; result = false; } // an error occurred if (!result) { // backup the problematic file String backupName = FileUtil.backup(xmlFilename); System.err.println("Error while loading Sequence XML persistent data."); System.err.println("The faulty file '" + xmlFilename + "' has been backuped as '" + backupName); if (exc != null) IcyExceptionHandler.showErrorMessage(exc, true); return false; } } return true; } /** * Save XML persistent data.<br> * Return true if XML data has been correctly saved. */ public boolean saveXMLData() throws Exception { final String xmlFilename = getXMLFileName(); if (xmlFilename == null) return false; // rebuild document refreshXMLData(); // save xml file return XMLUtil.saveDocument(document, xmlFilename); } public void refreshXMLData() { // force the new format when we save the XML saveToXML(getRootNode()); } @Override public boolean loadFromXML(Node node) { boolean result = true; final String name = XMLUtil.getElementValue(node, Sequence.ID_NAME, ""); // set name only if not empty if (!StringUtil.isEmpty(name)) sequence.setName(name); if (!loadMetaDataFromXML(node)) result = false; if (!loadROIsFromXML(node)) result = false; // some overlays does not support persistence so we can ignore errors... loadOverlaysFromXML(node); if (!loadLUTFromXML(node)) result = false; return result; } private boolean loadMetaDataFromXML(Node node) { final Node nodeMeta = XMLUtil.getElement(node, ID_META); // no node --> nothing to load... if (nodeMeta == null) return true; sequence.setPositionX(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_POSITION_X, 0d)); sequence.setPositionY(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Y, 0d)); sequence.setPositionZ(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Z, 0d)); sequence.setPixelSizeX(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_X, 1d)); sequence.setPixelSizeY(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Y, 1d)); sequence.setPixelSizeZ(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Z, 1d)); sequence.setTimeInterval(XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_TIME_INTERVAL, 1d)); for (int c = 0; c < sequence.getSizeC(); c++) sequence.setChannelName( c, XMLUtil.getElementValue(nodeMeta, Sequence.ID_CHANNEL_NAME + c, MetaDataUtil.DEFAULT_CHANNEL_NAME + c)); return true; } private boolean loadROIsFromXML(Node node) { final Node roisNode = XMLUtil.getElement(node, ID_ROIS); // no node --> nothing to load... if (roisNode == null) return true; final int roiCount = ROI.getROICount(roisNode); final List<ROI> rois = ROI.loadROIsFromXML(roisNode); // add to sequence for (ROI roi : rois) sequence.addROI(roi); // return true if we got the expected number of ROI return (roiCount == rois.size()); } private boolean loadOverlaysFromXML(Node node) { final Node overlaysNode = XMLUtil.getElement(node, ID_OVERLAYS); // no node --> nothing to load... if (overlaysNode == null) return true; final int overlayCount = Overlay.getOverlayCount(overlaysNode); final List<Overlay> overlays = Overlay.loadOverlaysFromXML(overlaysNode); // add to sequence for (Overlay overlay : overlays) sequence.addOverlay(overlay); // return true if we got the expected number of ROI return (overlayCount == overlays.size()); } private boolean loadLUTFromXML(Node node) { final Node nodeLut = XMLUtil.getElement(node, ID_LUT); // no node --> nothing to load... if (nodeLut == null) return true; // use the default LUT by default final LUT result = sequence.getDefaultLUT(); if (result.loadFromXML(nodeLut)) sequence.setUserLUT(result); return true; } @Override public boolean saveToXML(Node node) { XMLUtil.setElementValue(node, Sequence.ID_NAME, sequence.getName()); saveMetaDataToXML(node); saveROIsToXML(node); saveOverlaysToXML(node); saveLUTToXML(node); return true; } private void saveMetaDataToXML(Node node) { final Node nodeMeta = XMLUtil.setElement(node, ID_META); if (nodeMeta != null) { XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_POSITION_X, sequence.getPositionX()); XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Y, sequence.getPositionY()); XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Z, sequence.getPositionZ()); XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_X, sequence.getPixelSizeX()); XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Y, sequence.getPixelSizeY()); XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Z, sequence.getPixelSizeZ()); XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_TIME_INTERVAL, sequence.getTimeInterval()); for (int c = 0; c < sequence.getSizeC(); c++) XMLUtil.setElementValue(nodeMeta, Sequence.ID_CHANNEL_NAME + c, sequence.getChannelName(c)); } } private void saveROIsToXML(Node node) { final Node nodeROIs = XMLUtil.setElement(node, ID_ROIS); if (nodeROIs != null) { XMLUtil.removeAllChildren(nodeROIs); // get sorted ROIs final List<ROI> rois = sequence.getROIs(true); // set rois in the XML node ROI.saveROIsToXML(nodeROIs, rois); } } private void saveOverlaysToXML(Node node) { final Node nodeOverlays = XMLUtil.setElement(node, ID_OVERLAYS); if (nodeOverlays != null) { XMLUtil.removeAllChildren(nodeOverlays); // get overlays in linked list for faster remove operation final List<Overlay> overlays = new LinkedList<Overlay>(sequence.getOverlays()); // remove overlays from ROI as they are be automatically created from ROI for (ROI roi : sequence.getROIs(false)) overlays.remove(roi.getOverlay()); // set overlays in the XML node Overlay.saveOverlaysToXML(nodeOverlays, overlays); } } private void saveLUTToXML(Node node) { // save only if we have a custom LUT if (sequence.hasUserLUT()) { final LUT lut = sequence.getUserLUT(); // something to save ? if (lut != null) { final Node nodeLut = XMLUtil.setElement(node, ID_LUT); if (nodeLut != null) { XMLUtil.removeAllChildren(nodeLut); lut.saveToXML(nodeLut); } } } } /** * Get Sequence XML root node */ public Node getRootNode() { return XMLUtil.getRootElement(document); } /** * Get XML data node identified by specified name * * @param name * name of wanted node */ public Node getNode(String name) { return XMLUtil.getChild(getRootNode(), name); } /** * Create a new node with specified name and return it.<br> * If the node already exists the existing node is returned. * * @param name * name of node to set in attached XML data */ public Node setNode(String name) { return XMLUtil.setElement(getRootNode(), name); } /** * Returns <code>true</code> if the specified Document represents a valid XML persistence document. */ public static boolean isValidXMLPersitence(Document doc) { if (doc == null) return false; final Element rootNode = XMLUtil.getRootElement(doc); return (XMLUtil.getElement(rootNode, Sequence.ID_NAME) != null) && (XMLUtil.getElement(rootNode, ID_META) != null) && (XMLUtil.getElement(rootNode, ID_ROIS) != null) && (XMLUtil.getElement(rootNode, ID_OVERLAYS) != null); } /** * Returns <code>true</code> if the specified path represents a valid XML persistence file. */ public static boolean isValidXMLPersitence(String path) { if ((path != null) && FileUtil.exists(path)) { try { return isValidXMLPersitence(XMLUtil.loadDocument(path, true)); } catch (Exception e) { // ignore } } return false; } }