001/*- 002 * Copyright 2015, 2016 Diamond Light Source Ltd. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 */ 009 010package org.eclipse.january.dataset; 011 012import java.io.IOException; 013import java.util.Arrays; 014 015import org.eclipse.january.DatasetException; 016import org.eclipse.january.IMonitor; 017import org.eclipse.january.io.ILazyAsyncSaver; 018import org.eclipse.january.io.ILazySaver; 019 020/** 021 * Subclass of lazy dataset that allows setting slices 022 */ 023public class LazyWriteableDataset extends LazyDynamicDataset implements ILazyWriteableDataset { 024 private static final long serialVersionUID = -679846418938412535L; 025 private int[] chunks; 026 private ILazySaver saver; 027 private Object fillValue; 028 private boolean writeAsync; 029 030 /** 031 * Create a lazy dataset 032 * @param name 033 * @param dtype dataset type 034 * @param elements 035 * @param shape 036 * @param maxShape 037 * @param chunks 038 * @param saver 039 */ 040 public LazyWriteableDataset(String name, int dtype, int elements, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) { 041 super(name, dtype, elements, shape, maxShape, saver); 042 this.chunks = chunks == null ? null : chunks.clone(); 043 this.saver = saver; 044 045 size = ShapeUtils.calcLongSize(this.shape); 046 } 047 048 /** 049 * Create a lazy dataset 050 * @param name 051 * @param dtype dataset type 052 * @param shape 053 * @param maxShape 054 * @param chunks 055 * @param saver 056 */ 057 public LazyWriteableDataset(String name, int dtype, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) { 058 this(name, dtype, 1, shape, maxShape, chunks, saver); 059 } 060 061 /** 062 * Create a lazy dataset 063 * @param name 064 * @param clazz dataset element class 065 * @param elements 066 * @param shape 067 * @param maxShape 068 * @param chunks 069 * @param saver 070 */ 071 public LazyWriteableDataset(String name, Class<?> clazz, int elements, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) { 072 this(name, DTypeUtils.getDTypeFromClass(clazz), elements, shape, maxShape, chunks, saver); 073 } 074 075 /** 076 * Create a lazy dataset 077 * @param name 078 * @param clazz dataset element class 079 * @param shape 080 * @param maxShape 081 * @param chunks 082 * @param saver 083 */ 084 public LazyWriteableDataset(String name, Class<?> clazz, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) { 085 this(name, DTypeUtils.getDTypeFromClass(clazz), 1, shape, maxShape, chunks, saver); 086 } 087 088 /** 089 * @since 2.2 090 */ 091 protected LazyWriteableDataset(LazyWriteableDataset other) { 092 super(other); 093 094 chunks = other.chunks; 095 saver = other.saver; 096 fillValue = other.fillValue; 097 writeAsync = other.writeAsync; 098 } 099 100 /** 101 * Create a lazy writeable dataset based on in-memory data (handy for testing) 102 * @param dataset 103 */ 104 public static LazyWriteableDataset createLazyDataset(final Dataset dataset) { 105 return createLazyDataset(dataset, null); 106 } 107 108 /** 109 * Create a lazy writeable dataset based on in-memory data (handy for testing) 110 * @param dataset 111 */ 112 public static LazyWriteableDataset createLazyDataset(final Dataset dataset, final int[] maxShape) { 113 return new LazyWriteableDataset(dataset.getName(), dataset.getDType(), dataset.getElementsPerItem(), dataset.getShapeRef(), 114 maxShape, null, 115 new ILazySaver() { 116 private static final long serialVersionUID = ILazySaver.serialVersionUID; 117 118 Dataset d = dataset; 119 @Override 120 public boolean isFileReadable() { 121 return true; 122 } 123 124 @Override 125 public boolean isFileWriteable() { 126 return true; 127 } 128 129 @Override 130 public void initialize() throws IOException { 131 } 132 133 @Override 134 public Dataset getDataset(IMonitor mon, SliceND slice) throws IOException { 135 return d.getSlice(mon, slice); 136 } 137 138 @Override 139 public void setSlice(IMonitor mon, IDataset data, SliceND slice) throws IOException { 140 if (slice.isExpanded()) { 141 Dataset od = d; 142 d = DatasetFactory.zeros(od.getClass(), slice.getSourceShape()); 143 d.setSlice(od, SliceND.createSlice(od, null, null)); 144 } 145 d.setSlice(data, slice); 146 } 147 }); 148 } 149 150 @Override 151 public int hashCode() { 152 final int prime = 31; 153 int result = super.hashCode(); 154 result = prime * result + Arrays.hashCode(chunks); 155 result = prime * result + ((fillValue == null) ? 0 : fillValue.hashCode()); 156 result = prime * result + (writeAsync ? 1231 : 1237); 157 return result; 158 } 159 160 @Override 161 public boolean equals(Object obj) { 162 if (this == obj) { 163 return true; 164 } 165 if (!super.equals(obj)) { 166 return false; 167 } 168 169 LazyWriteableDataset other = (LazyWriteableDataset) obj; 170 if (!Arrays.equals(chunks, other.chunks)) { 171 return false; 172 } 173 if (fillValue == null) { 174 if (other.fillValue != null) { 175 return false; 176 } 177 } else if (!fillValue.equals(other.fillValue)) { 178 return false; 179 } 180 if (saver == null) { 181 if (other.saver != null) { 182 return false; 183 } 184 } else if (!saver.equals(other.saver)) { 185 return false; 186 } 187 if (writeAsync != other.writeAsync) { 188 return false; 189 } 190 191 return true; 192 } 193 194 @Override 195 public int[] getChunking() { 196 return chunks; 197 } 198 199 @Override 200 public void setChunking(int... chunks) { 201 this.chunks = chunks == null ? null : chunks.clone(); 202 } 203 204 @Override 205 public LazyWriteableDataset clone() { 206 return new LazyWriteableDataset(this); 207 } 208 209 @Override 210 public LazyWriteableDataset getSliceView(int[] start, int[] stop, int[] step) { 211 return (LazyWriteableDataset) super.getSliceView(start, stop, step); 212 } 213 214 @Override 215 public LazyWriteableDataset getSliceView(Slice... slice) { 216 return (LazyWriteableDataset) super.getSliceView(slice); 217 } 218 219 @Override 220 public LazyWriteableDataset getSliceView(SliceND slice) { 221 return (LazyWriteableDataset) super.getSliceView(slice); 222 } 223 224 @Override 225 public LazyWriteableDataset getTransposedView(int... axes) { 226 return (LazyWriteableDataset) super.getTransposedView(axes); 227 } 228 229 @Override 230 public void setWritingAsync(boolean async) { 231 writeAsync = async; 232 } 233 234 /** 235 * Set a slice of the dataset 236 * 237 * @param data 238 * @param slice an n-D slice 239 * @throws DatasetException 240 */ 241 public void setSlice(IDataset data, SliceND slice) throws DatasetException { 242 setSlice(null, data, slice); 243 } 244 245 @Override 246 public void setSlice(IMonitor monitor, IDataset data, int[] start, int[] stop, int[] step) throws DatasetException { 247 internalSetSlice(monitor, writeAsync, data, new SliceND(shape, maxShape, start, stop, step)); 248 } 249 250 @Override 251 public void setSlice(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException { 252 internalSetSlice(monitor, writeAsync, data, slice); 253 } 254 255 @Override 256 public void setSliceSync(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException { 257 internalSetSlice(monitor, false, data, slice); 258 } 259 260 private void internalSetSlice(IMonitor monitor, final boolean async, IDataset data, SliceND slice) throws DatasetException { 261 int[] dshape = data instanceof Dataset ? ((Dataset) data).getShapeRef() : data.getShape(); 262 if (dshape.length == 0) { // fix zero-rank case 263 dshape = new int[] {1}; // FIXME remove 264 } 265 // if necessary, reshape the input data according to the shape of the slice 266 if (!Arrays.equals(slice.getShape(), dshape)) { 267 data = data.getSliceView(); 268 data.setShape(slice.getShape()); 269 } 270 271 SliceND nslice = calcTrueSlice(slice); 272 if (nslice == null) { 273 return; // nothing to set 274 } 275 276 data = transformInput(data, slice); 277 278 if (saver == null) { 279 throw new DatasetException("Cannot write to file as saver not defined!"); 280 } 281 282 try { 283 if (async && saver instanceof ILazyAsyncSaver) { 284 ((ILazyAsyncSaver)saver).setSliceAsync(monitor, data, nslice); 285 } else { 286 if (!saver.isFileWriteable()) { 287 throw new DatasetException("Cannot write to file as it is not writeable!"); 288 } 289 saver.setSlice(monitor, data, nslice); 290 } 291 } catch (IOException e) { 292 throw new DatasetException("Could not save dataset", e); 293 } 294 if (!refreshShape()) { // send event as data has changed 295 eventDelegate.fire(new DataEvent(name, shape)); 296 } 297 } 298 299 /** 300 * Set saver (and also loader) 301 * @param saver 302 */ 303 @Override 304 public void setSaver(ILazySaver saver) { 305 this.saver = saver; 306 this.loader = saver; 307 } 308 309 @Override 310 protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) { 311 return SliceND.createSlice(oShape, maxShape, nstart, nstop, nstep); 312 } 313 314 @Override 315 public Object getFillValue() { 316 return fillValue; 317 } 318 319 @Override 320 public void setFillValue(Object fill) { 321 fillValue = fill; 322 } 323}