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}