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.getShape(), 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 (!super.equals(obj)) { 163 return false; 164 } 165 166 LazyWriteableDataset other = (LazyWriteableDataset) obj; 167 if (!Arrays.equals(chunks, other.chunks)) { 168 return false; 169 } 170 if (fillValue == null) { 171 if (other.fillValue != null) { 172 return false; 173 } 174 } else if (!fillValue.equals(other.fillValue)) { 175 return false; 176 } 177 if (saver == null) { 178 if (other.saver != null) { 179 return false; 180 } 181 } else if (!saver.equals(other.saver)) { 182 return false; 183 } 184 if (writeAsync != other.writeAsync) { 185 return false; 186 } 187 188 return true; 189 } 190 191 @Override 192 public int[] getChunking() { 193 return chunks; 194 } 195 196 @Override 197 public void setChunking(int... chunks) { 198 this.chunks = chunks == null ? null : chunks.clone(); 199 } 200 201 @Override 202 public LazyWriteableDataset clone() { 203 return new LazyWriteableDataset(this); 204 } 205 206 @Override 207 public LazyWriteableDataset getSliceView(int[] start, int[] stop, int[] step) { 208 return (LazyWriteableDataset) super.getSliceView(start, stop, step); 209 } 210 211 @Override 212 public LazyWriteableDataset getSliceView(Slice... slice) { 213 return (LazyWriteableDataset) super.getSliceView(slice); 214 } 215 216 @Override 217 public LazyWriteableDataset getSliceView(SliceND slice) { 218 return (LazyWriteableDataset) super.getSliceView(slice); 219 } 220 221 @Override 222 public LazyWriteableDataset getTransposedView(int... axes) { 223 return (LazyWriteableDataset) super.getTransposedView(axes); 224 } 225 226 @Override 227 public void setWritingAsync(boolean async) { 228 writeAsync = async; 229 } 230 231 /** 232 * Set a slice of the dataset 233 * 234 * @param data 235 * @param slice an n-D slice 236 * @throws DatasetException 237 */ 238 public void setSlice(IDataset data, SliceND slice) throws DatasetException { 239 setSlice(null, data, slice); 240 } 241 242 @Override 243 public void setSlice(IMonitor monitor, IDataset data, int[] start, int[] stop, int[] step) throws DatasetException { 244 internalSetSlice(monitor, writeAsync, data, new SliceND(shape, maxShape, start, stop, step)); 245 } 246 247 @Override 248 public void setSlice(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException { 249 internalSetSlice(monitor, writeAsync, data, slice); 250 } 251 252 @Override 253 public void setSliceSync(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException { 254 internalSetSlice(monitor, false, data, slice); 255 } 256 257 private void internalSetSlice(IMonitor monitor, final boolean async, IDataset data, SliceND slice) throws DatasetException { 258 int[] dshape = data instanceof Dataset ? ((Dataset) data).getShapeRef() : data.getShape(); 259 if (dshape.length == 0) { // fix zero-rank case 260 dshape = new int[] {1}; // FIXME remove 261 } 262 // if necessary, reshape the input data according to the shape of the slice 263 if (!Arrays.equals(slice.getShape(), dshape)) { 264 data = data.getSliceView(); 265 data.setShape(slice.getShape()); 266 } 267 268 SliceND nslice = calcTrueSlice(slice); 269 if (nslice == null) { 270 return; // nothing to set 271 } 272 273 data = transformInput(data, slice); 274 275 if (saver == null) { 276 throw new DatasetException("Cannot write to file as saver not defined!"); 277 } 278 279 try { 280 if (async && saver instanceof ILazyAsyncSaver) { 281 ((ILazyAsyncSaver)saver).setSliceAsync(monitor, data, nslice); 282 } else { 283 if (!saver.isFileWriteable()) { 284 throw new DatasetException("Cannot write to file as it is not writeable!"); 285 } 286 saver.setSlice(monitor, data, nslice); 287 } 288 } catch (IOException e) { 289 throw new DatasetException("Could not save dataset", e); 290 } 291 if (!refreshShape()) { // send event as data has changed 292 eventDelegate.fire(new DataEvent(name, shape)); 293 } 294 } 295 296 /** 297 * Set saver (and also loader) 298 * @param saver 299 */ 300 @Override 301 public void setSaver(ILazySaver saver) { 302 this.saver = saver; 303 this.loader = saver; 304 } 305 306 @Override 307 protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) { 308 return SliceND.createSlice(oShape, maxShape, nstart, nstop, nstep); 309 } 310 311 @Override 312 public Object getFillValue() { 313 return fillValue; 314 } 315 316 @Override 317 public void setFillValue(Object fill) { 318 fillValue = fill; 319 } 320}