001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.sequence; 020 021import icy.file.FileUtil; 022import icy.file.xml.XMLPersistent; 023import icy.image.lut.LUT; 024import icy.painter.Overlay; 025import icy.roi.ROI; 026import icy.system.IcyExceptionHandler; 027import icy.util.StringUtil; 028import icy.util.XMLUtil; 029 030import java.util.LinkedList; 031import java.util.List; 032 033import org.w3c.dom.Document; 034import org.w3c.dom.Element; 035import org.w3c.dom.Node; 036 037/** 038 * @author Stephane 039 */ 040public class SequencePersistent implements XMLPersistent 041{ 042 private final static String ID_META = "meta"; 043 private final static String ID_ROIS = "rois"; 044 private final static String ID_OVERLAYS = "overlays"; 045 private final static String ID_LUT = "lut"; 046 047 private final Sequence sequence; 048 049 private Document document; 050 051 /** 052 * 053 */ 054 public SequencePersistent(Sequence sequence) 055 { 056 super(); 057 058 this.sequence = sequence; 059 060 document = XMLUtil.createDocument(true); 061 } 062 063 /** 064 * Should return <code>null</code> if Sequence is not identified (no file name) 065 */ 066 private String getXMLFileName() 067 { 068 final String baseName = sequence.getOutputFilename(false); 069 070 if (StringUtil.isEmpty(baseName)) 071 return null; 072 073 return baseName + XMLUtil.FILE_DOT_EXTENSION; 074 } 075 076 /** 077 * Load XML persistent data.<br> 078 * Return true if XML data has been correctly loaded. 079 */ 080 public boolean loadXMLData() 081 { 082 final String xmlFilename = getXMLFileName(); 083 boolean result; 084 Exception exc = null; 085 086 if ((xmlFilename != null) && FileUtil.exists(xmlFilename)) 087 { 088 try 089 { 090 // load xml file into document 091 document = XMLUtil.loadDocument(xmlFilename, true); 092 093 // load data from XML document 094 if (document != null) 095 result = loadFromXML(getRootNode()); 096 else 097 { 098 document = XMLUtil.createDocument(true); 099 result = false; 100 } 101 } 102 catch (Exception e) 103 { 104 exc = e; 105 result = false; 106 } 107 108 // an error occurred 109 if (!result) 110 { 111 // backup the problematic file 112 String backupName = FileUtil.backup(xmlFilename); 113 114 System.err.println("Error while loading Sequence XML persistent data."); 115 System.err.println("The faulty file '" + xmlFilename + "' has been backuped as '" + backupName); 116 117 if (exc != null) 118 IcyExceptionHandler.showErrorMessage(exc, true); 119 120 return false; 121 } 122 } 123 124 return true; 125 } 126 127 /** 128 * Save XML persistent data.<br> 129 * Return true if XML data has been correctly saved. 130 */ 131 public boolean saveXMLData() throws Exception 132 { 133 final String xmlFilename = getXMLFileName(); 134 135 if (xmlFilename == null) 136 return false; 137 138 // rebuild document 139 refreshXMLData(); 140 141 // save xml file 142 return XMLUtil.saveDocument(document, xmlFilename); 143 } 144 145 public void refreshXMLData() 146 { 147 // force the new format when we save the XML 148 saveToXML(getRootNode()); 149 } 150 151 @Override 152 public boolean loadFromXML(Node node) 153 { 154 boolean result = true; 155 final String name = XMLUtil.getElementValue(node, Sequence.ID_NAME, ""); 156 157 // set name only if not empty 158 if (!StringUtil.isEmpty(name)) 159 sequence.setName(name); 160 161 if (!loadMetaDataFromXML(node)) 162 result = false; 163 if (!loadROIsFromXML(node)) 164 result = false; 165 // some overlays does not support persistence so we can ignore errors... 166 loadOverlaysFromXML(node); 167 if (!loadLUTFromXML(node)) 168 result = false; 169 170 return result; 171 } 172 173 private boolean loadMetaDataFromXML(Node node) 174 { 175 final Node nodeMeta = XMLUtil.getElement(node, ID_META); 176 177 // no node --> nothing to load... 178 if (nodeMeta == null) 179 return true; 180 181 double d; 182 long l; 183 184 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_POSITION_X, Double.NaN); 185 if (!Double.isNaN(d)) 186 sequence.setPositionX(d); 187 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Y, Double.NaN); 188 if (!Double.isNaN(d)) 189 sequence.setPositionY(d); 190 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Z, Double.NaN); 191 if (!Double.isNaN(d)) 192 sequence.setPositionZ(d); 193 l = XMLUtil.getElementLongValue(nodeMeta, Sequence.ID_POSITION_T, -1L); 194 if (l != -1L) 195 sequence.setPositionT(l); 196 197 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_X, Double.NaN); 198 if (!Double.isNaN(d)) 199 sequence.setPixelSizeX(d); 200 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Y, Double.NaN); 201 if (!Double.isNaN(d)) 202 sequence.setPixelSizeY(d); 203 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Z, Double.NaN); 204 if (!Double.isNaN(d)) 205 sequence.setPixelSizeZ(d); 206 d = XMLUtil.getElementDoubleValue(nodeMeta, Sequence.ID_TIME_INTERVAL, Double.NaN); 207 if (!Double.isNaN(d)) 208 sequence.setTimeInterval(d); 209 210 for (int c = 0; c < sequence.getSizeC(); c++) 211 { 212 final String s = XMLUtil.getElementValue(nodeMeta, Sequence.ID_CHANNEL_NAME + c, ""); 213 214 if (!StringUtil.isEmpty(s)) 215 sequence.setChannelName(c, s); 216 } 217 218 return true; 219 } 220 221 private boolean loadROIsFromXML(Node node) 222 { 223 final Node roisNode = XMLUtil.getElement(node, ID_ROIS); 224 225 // no node --> nothing to load... 226 if (roisNode == null) 227 return true; 228 229 final int roiCount = ROI.getROICount(roisNode); 230 final List<ROI> rois = ROI.loadROIsFromXML(roisNode); 231 232 // add to sequence 233 for (ROI roi : rois) 234 sequence.addROI(roi); 235 236 // return true if we got the expected number of ROI 237 return (roiCount == rois.size()); 238 } 239 240 private boolean loadOverlaysFromXML(Node node) 241 { 242 final Node overlaysNode = XMLUtil.getElement(node, ID_OVERLAYS); 243 244 // no node --> nothing to load... 245 if (overlaysNode == null) 246 return true; 247 248 final int overlayCount = Overlay.getOverlayCount(overlaysNode); 249 final List<Overlay> overlays = Overlay.loadOverlaysFromXML(overlaysNode); 250 251 // add to sequence 252 for (Overlay overlay : overlays) 253 sequence.addOverlay(overlay); 254 255 // return true if we got the expected number of ROI 256 return (overlayCount == overlays.size()); 257 } 258 259 private boolean loadLUTFromXML(Node node) 260 { 261 final Node nodeLut = XMLUtil.getElement(node, ID_LUT); 262 263 // no node --> nothing to load... 264 if (nodeLut == null) 265 return true; 266 267 // use the default LUT by default 268 final LUT result = sequence.createCompatibleLUT(); 269 270 if (result.loadFromXML(nodeLut)) 271 sequence.setUserLUT(result); 272 273 return true; 274 } 275 276 @Override 277 public boolean saveToXML(Node node) 278 { 279 XMLUtil.setElementValue(node, Sequence.ID_NAME, sequence.getName()); 280 281 saveMetaDataToXML(node); 282 saveROIsToXML(node); 283 saveOverlaysToXML(node); 284 saveLUTToXML(node); 285 286 return true; 287 } 288 289 private void saveMetaDataToXML(Node node) 290 { 291 final Node nodeMeta = XMLUtil.setElement(node, ID_META); 292 293 if (nodeMeta != null) 294 { 295 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_POSITION_X, sequence.getPositionX()); 296 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Y, sequence.getPositionY()); 297 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_POSITION_Z, sequence.getPositionZ()); 298 XMLUtil.setElementLongValue(nodeMeta, Sequence.ID_POSITION_T, sequence.getPositionT()); 299 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_X, sequence.getPixelSizeX()); 300 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Y, sequence.getPixelSizeY()); 301 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_PIXEL_SIZE_Z, sequence.getPixelSizeZ()); 302 XMLUtil.setElementDoubleValue(nodeMeta, Sequence.ID_TIME_INTERVAL, sequence.getTimeInterval()); 303 304 for (int c = 0; c < sequence.getSizeC(); c++) 305 XMLUtil.setElementValue(nodeMeta, Sequence.ID_CHANNEL_NAME + c, sequence.getChannelName(c)); 306 } 307 } 308 309 private void saveROIsToXML(Node node) 310 { 311 final Node nodeROIs = XMLUtil.setElement(node, ID_ROIS); 312 313 if (nodeROIs != null) 314 { 315 XMLUtil.removeAllChildren(nodeROIs); 316 317 // get sorted ROIs 318 final List<ROI> rois = sequence.getROIs(true); 319 320 // set rois in the XML node 321 ROI.saveROIsToXML(nodeROIs, rois); 322 } 323 } 324 325 private void saveOverlaysToXML(Node node) 326 { 327 final Node nodeOverlays = XMLUtil.setElement(node, ID_OVERLAYS); 328 329 if (nodeOverlays != null) 330 { 331 XMLUtil.removeAllChildren(nodeOverlays); 332 333 // get overlays in linked list for faster remove operation 334 final List<Overlay> overlays = new LinkedList<Overlay>(sequence.getOverlays()); 335 // remove overlays from ROI as they are be automatically created from ROI 336 for (ROI roi : sequence.getROIs(false)) 337 overlays.remove(roi.getOverlay()); 338 339 // set overlays in the XML node 340 Overlay.saveOverlaysToXML(nodeOverlays, overlays); 341 } 342 } 343 344 private void saveLUTToXML(Node node) 345 { 346 // save only if we have a custom LUT 347 if (sequence.hasUserLUT()) 348 { 349 final LUT lut = sequence.getUserLUT(); 350 351 // something to save ? 352 if (lut != null) 353 { 354 final Node nodeLut = XMLUtil.setElement(node, ID_LUT); 355 356 if (nodeLut != null) 357 { 358 XMLUtil.removeAllChildren(nodeLut); 359 lut.saveToXML(nodeLut); 360 } 361 } 362 } 363 } 364 365 /** 366 * Get Sequence XML root node 367 */ 368 public Node getRootNode() 369 { 370 return XMLUtil.getRootElement(document); 371 } 372 373 /** 374 * Get XML data node identified by specified name 375 * 376 * @param name 377 * name of wanted node 378 */ 379 public Node getNode(String name) 380 { 381 return XMLUtil.getChild(getRootNode(), name); 382 } 383 384 /** 385 * Create a new node with specified name and return it.<br> 386 * If the node already exists the existing node is returned. 387 * 388 * @param name 389 * name of node to set in attached XML data 390 */ 391 public Node setNode(String name) 392 { 393 return XMLUtil.setElement(getRootNode(), name); 394 } 395 396 /** 397 * Returns <code>true</code> if the specified Document represents a valid XML persistence document. 398 */ 399 public static boolean isValidXMLPersitence(Document doc) 400 { 401 if (doc == null) 402 return false; 403 404 final Element rootNode = XMLUtil.getRootElement(doc); 405 406 return (XMLUtil.getElement(rootNode, Sequence.ID_NAME) != null) 407 && (XMLUtil.getElement(rootNode, ID_META) != null) && (XMLUtil.getElement(rootNode, ID_ROIS) != null) 408 && (XMLUtil.getElement(rootNode, ID_OVERLAYS) != null); 409 } 410 411 /** 412 * Returns <code>true</code> if the specified path represents a valid XML persistence file. 413 */ 414 public static boolean isValidXMLPersitence(String path) 415 { 416 if ((path != null) && FileUtil.exists(path)) 417 { 418 try 419 { 420 return isValidXMLPersitence(XMLUtil.loadDocument(path, true)); 421 } 422 catch (Exception e) 423 { 424 // ignore 425 } 426 } 427 428 return false; 429 } 430}