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}