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 of dataset
033         * @param dtype dataset type
034         * @param elements item size
035         * @param shape dataset shape
036         * @param maxShape maximum shape
037         * @param chunks chunk shape
038         * @param saver lazy saver
039         * @deprecated Use {@link #LazyWriteableDataset(ILazySaver, String, int, Class, int[], int[], int[])}
040         */
041        @Deprecated
042        public LazyWriteableDataset(String name, int dtype, int elements, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
043                this(saver, name, elements, DTypeUtils.getInterface(dtype), shape, maxShape, chunks);
044        }
045
046        /**
047         * Create a lazy dataset
048         * @param saver lazy saver
049         * @param name of dataset
050         * @param clazz dataset sub-interface
051         * @param shape dataset shape
052         * @param maxShape maximum shape
053         * @param chunks chunk shape
054         * @since 2.3
055         */
056        public LazyWriteableDataset(ILazySaver saver, String name, Class<? extends Dataset> clazz, int[] shape, int[] maxShape, int[] chunks) {
057                this(saver, name, 1, clazz, shape, maxShape, chunks);
058        }
059
060        /**
061         * Create a lazy dataset
062         * @param saver lazy saver
063         * @param name of dataset
064         * @param elements item size
065         * @param clazz dataset sub-interface
066         * @param shape dataset shape
067         * @param maxShape maximum shape
068         * @param chunks chunk shape
069         * @since 2.3
070         */
071        public LazyWriteableDataset(ILazySaver saver, String name, int elements, Class<? extends Dataset> clazz, int[] shape, int[] maxShape, int[] chunks) {
072                super(saver, name, elements, clazz, shape, maxShape);
073                this.chunks = chunks == null ? null : chunks.clone();
074                this.saver = saver;
075
076                size = ShapeUtils.calcLongSize(this.shape);
077        }
078
079        /**
080         * Create a lazy dataset
081         * @param name of dataset
082         * @param dtype dataset type
083         * @param shape dataset shape
084         * @param maxShape maximum shape
085         * @param chunks chunk shape
086         * @param saver lazy saver
087         * @deprecated Use {@link #LazyWriteableDataset(ILazySaver, String, int, Class, int[], int[], int[])}
088         */
089        @Deprecated
090        public LazyWriteableDataset(String name, int dtype, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
091                this(name, dtype, 1, shape, maxShape, chunks, saver);
092        }
093
094        /**
095         * Create a lazy dataset using element class
096         * @param name of dataset
097         * @param eClass element class
098         * @param elements item size
099         * @param shape dataset shape
100         * @param maxShape maximum shape
101         * @param chunks chunk shape
102         * @param saver lazy saver
103         */
104        public LazyWriteableDataset(String name, Class<?> eClass, int elements, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
105                this(saver, name, elements, InterfaceUtils.getInterfaceFromClass(elements, eClass), shape, maxShape, chunks);
106        }
107
108        /**
109         * Create a lazy dataset using element class
110         * @param name of dataset
111         * @param eClass element class
112         * @param shape dataset shape
113         * @param maxShape maximum shape
114         * @param chunks chunk shape
115         * @param saver lazy saver
116         */
117        public LazyWriteableDataset(String name, Class<?> eClass, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
118                this(saver, name, 1, InterfaceUtils.getInterfaceFromClass(1, eClass), shape, maxShape, chunks);
119        }
120
121        /**
122         * @param other dataset
123         * @since 2.2
124         */
125        protected LazyWriteableDataset(LazyWriteableDataset other) {
126                super(other);
127
128                chunks = other.chunks;
129                saver  = other.saver;
130                fillValue  = other.fillValue;
131                writeAsync = other.writeAsync;
132        }
133
134        /**
135         * Create a lazy writeable dataset based on in-memory data (handy for testing)
136         * @param dataset input
137         * @return lazy writeable dataset
138         */
139        public static LazyWriteableDataset createLazyDataset(final Dataset dataset) {
140                return createLazyDataset(dataset, null);
141        }
142
143        /**
144         * Create a lazy writeable dataset based on in-memory data (handy for testing)
145         * @param dataset input
146         * @param maxShape maximum shape
147         * @return lazy writeable dataset
148         */
149        public static LazyWriteableDataset createLazyDataset(final Dataset dataset, final int[] maxShape) {
150                return new LazyWriteableDataset(new ILazySaver() {
151                        private static final long serialVersionUID = ILazySaver.serialVersionUID;
152
153                        Dataset d = dataset;
154                        @Override
155                        public boolean isFileReadable() {
156                                return true;
157                        }
158
159                        @Override
160                        public boolean isFileWriteable() {
161                                return true;
162                        }
163
164                        @Override
165                        public void initialize() throws IOException {
166                        }
167
168                        @Override
169                        public Dataset getDataset(IMonitor mon, SliceND slice) throws IOException {
170                                return d.getSlice(mon, slice);
171                        }
172
173                        @Override
174                        public void setSlice(IMonitor mon, IDataset data, SliceND slice) throws IOException {
175                                if (slice.isExpanded()) {
176                                        Dataset od = d;
177                                        d = DatasetFactory.zeros(od.getClass(), slice.getSourceShape());
178                                        d.setSlice(od, SliceND.createSlice(d, null, od.getShapeRef()));
179                                }
180                                d.setSlice(data, slice);
181                        }
182                },
183                dataset.getName(), dataset.getElementsPerItem(), dataset.getClass(), dataset.getShapeRef(), maxShape, null);
184        }
185
186        @Override
187        public int hashCode() {
188                final int prime = 31;
189                int result = super.hashCode();
190                result = prime * result + Arrays.hashCode(chunks);
191                result = prime * result + ((fillValue == null) ? 0 : fillValue.hashCode());
192                result = prime * result + (writeAsync ? 1231 : 1237);
193                return result;
194        }
195
196        @Override
197        public boolean equals(Object obj) {
198                if (this == obj) {
199                        return true;
200                }
201                if (!super.equals(obj)) {
202                        return false;
203                }
204
205                LazyWriteableDataset other = (LazyWriteableDataset) obj;
206                if (!Arrays.equals(chunks, other.chunks)) {
207                        return false;
208                }
209                if (fillValue == null) {
210                        if (other.fillValue != null) {
211                                return false;
212                        }
213                } else if (!fillValue.equals(other.fillValue)) {
214                        return false;
215                }
216                if (saver == null) {
217                        if (other.saver != null) {
218                                return false;
219                        }
220                } else if (!saver.equals(other.saver)) {
221                        return false;
222                }
223                if (writeAsync != other.writeAsync) {
224                        return false;
225                }
226
227                return true;
228        }
229
230        @Override
231        public int[] getChunking() {
232                return chunks;
233        }
234
235        @Override
236        public void setChunking(int... chunks) {
237                this.chunks = chunks == null ? null : chunks.clone();
238        }
239
240        @Override
241        public LazyWriteableDataset clone() {
242                return new LazyWriteableDataset(this);
243        }
244
245        @Override
246        public LazyWriteableDataset getSliceView(int[] start, int[] stop, int[] step) {
247                return (LazyWriteableDataset) super.getSliceView(start, stop, step);
248        }
249
250        @Override
251        public LazyWriteableDataset getSliceView(Slice... slice) {
252                return (LazyWriteableDataset) super.getSliceView(slice);
253        }
254
255        @Override
256        public LazyWriteableDataset getSliceView(SliceND slice) {
257                return (LazyWriteableDataset) super.getSliceView(slice);
258        }
259
260        @Override
261        public LazyWriteableDataset getTransposedView(int... axes) {
262                return (LazyWriteableDataset) super.getTransposedView(axes);
263        }
264
265        @Override
266        public void setWritingAsync(boolean async) {
267                writeAsync = async;
268        }
269
270        /**
271         * Set a slice of the dataset
272         * 
273         * @param data to set
274         * @param slice an n-D slice
275         * @throws DatasetException when cannot write data
276         */
277        public void setSlice(IDataset data, SliceND slice) throws DatasetException {
278                setSlice(null, data, slice);
279        }
280
281        @Override
282        public void setSlice(IMonitor monitor, IDataset data, int[] start, int[] stop, int[] step) throws DatasetException {
283                internalSetSlice(monitor, writeAsync, data, new SliceND(shape, maxShape, start, stop, step));
284        }
285
286        @Override
287        public void setSlice(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
288                checkSliceND(slice);
289                internalSetSlice(monitor, writeAsync, data, slice);
290        }
291
292        @Override
293        public void setSliceSync(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
294                checkSliceND(slice);
295                internalSetSlice(monitor, false, data, slice);
296        }
297
298        private void internalSetSlice(IMonitor monitor, final boolean async, IDataset data, SliceND slice) throws DatasetException {
299                if (slice != null) {
300                        int[] dshape = data instanceof Dataset ? ((Dataset) data).getShapeRef() : data.getShape();
301
302                        // if necessary, reshape the input data according to the shape of the slice
303                        if (!Arrays.equals(slice.getShape(), dshape)) {
304                                data = data.getSliceView();
305                                data.setShape(slice.getShape());
306                        }
307                }
308
309                SliceND nslice = calcTrueSlice(slice);
310                if (nslice == null) {
311                        return; // nothing to set
312                }
313
314                data = transformInput(data, slice);
315
316                if (saver == null) {
317                        throw new DatasetException("Cannot write to file as saver not defined!");
318                }
319
320                try {
321                        if (async && saver instanceof ILazyAsyncSaver) {
322                                ((ILazyAsyncSaver)saver).setSliceAsync(monitor, data, nslice);
323                        } else {
324                                if (!saver.isFileWriteable()) {
325                                        throw new DatasetException("Cannot write to file as it is not writeable!");
326                                }
327                                saver.setSlice(monitor, data, nslice);
328                        }
329                } catch (IOException e) {
330                        throw new DatasetException("Could not save dataset", e);
331                }
332                if (!refreshShape()) { // send event as data has changed
333                        eventDelegate.fire(new DataEvent(name, shape));
334                }
335        }
336
337        /**
338         * Set saver (and also loader)
339         * @param saver lazy saver
340         */
341        @Override
342        public void setSaver(ILazySaver saver) {
343                this.saver = saver;
344                this.loader = saver;
345        }
346
347        @Override
348        protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) {
349                return SliceND.createSlice(oShape, maxShape, nstart, nstop, nstep);
350        }
351
352        @Override
353        public Object getFillValue() {
354                return fillValue;
355        }
356
357        @Override
358        public void setFillValue(Object fill) {
359                fillValue = fill;
360        }
361
362        @Override
363        public LazyWriteableDataset squeezeEnds() {
364                return (LazyWriteableDataset) super.squeezeEnds();
365        }
366}