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