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         * Create a lazy writeable dataset based on in-memory data (handy for testing)
090         * @param dataset
091         */
092        public static LazyWriteableDataset createLazyDataset(final Dataset dataset) {
093                return createLazyDataset(dataset, null);
094        }
095
096        /**
097         * Create a lazy writeable dataset based on in-memory data (handy for testing)
098         * @param dataset
099         */
100        public static LazyWriteableDataset createLazyDataset(final Dataset dataset, final int[] maxShape) {
101                return new LazyWriteableDataset(dataset.getName(), dataset.getDType(), dataset.getElementsPerItem(), dataset.getShape(),
102                                maxShape, null,
103                new ILazySaver() {
104                        private static final long serialVersionUID = ILazySaver.serialVersionUID;
105
106                        Dataset d = dataset;
107                        @Override
108                        public boolean isFileReadable() {
109                                return true;
110                        }
111
112                        @Override
113                        public boolean isFileWriteable() {
114                                return true;
115                        }
116
117                        @Override
118                        public void initialize() throws IOException {
119                        }
120
121                        @Override
122                        public Dataset getDataset(IMonitor mon, SliceND slice) throws IOException {
123                                return d.getSlice(mon, slice);
124                        }
125
126                        @SuppressWarnings("deprecation")
127                        @Override
128                        public void setSlice(IMonitor mon, IDataset data, SliceND slice) throws IOException {
129                                if (slice.isExpanded()) {
130                                        Dataset od = d;
131                                        d = DatasetFactory.zeros(slice.getSourceShape(), od.getDType());
132                                        d.setSlice(od, SliceND.createSlice(od, null, null));
133                                }
134                                d.setSlice(data, slice);
135                        }
136                });
137        }
138
139        @Override
140        public int[] getChunking() {
141                return chunks;
142        }
143
144        @Override
145        public void setChunking(int... chunks) {
146                this.chunks = chunks == null ? null : chunks.clone();
147        }
148
149        @Override
150        public LazyWriteableDataset clone() {
151                LazyWriteableDataset ret = new LazyWriteableDataset(new String(name), getDType(), getElementsPerItem(), 
152                                oShape, maxShape, chunks, saver);
153                ret.shape = shape;
154                ret.size = size;
155                ret.prepShape = prepShape;
156                ret.postShape = postShape;
157                ret.begSlice = begSlice;
158                ret.delSlice = delSlice;
159                ret.map = map;
160                ret.base = base;
161                ret.metadata = copyMetadata();
162                ret.oMetadata = oMetadata;
163                ret.eventDelegate = eventDelegate;
164                return ret;
165        }
166
167        @Override
168        public LazyWriteableDataset getSliceView(int[] start, int[] stop, int[] step) {
169                return (LazyWriteableDataset) super.getSliceView(start, stop, step);
170        }
171
172        @Override
173        public LazyWriteableDataset getSliceView(Slice... slice) {
174                return (LazyWriteableDataset) super.getSliceView(slice);
175        }
176
177        @Override
178        public LazyWriteableDataset getSliceView(SliceND slice) {
179                return (LazyWriteableDataset) super.getSliceView(slice);
180        }
181
182        @Override
183        public LazyWriteableDataset getTransposedView(int... axes) {
184                return (LazyWriteableDataset) super.getTransposedView(axes);
185        }
186
187        @Override
188        public void setWritingAsync(boolean async) {
189                writeAsync = async;
190        }
191
192        /**
193         * Set a slice of the dataset
194         * 
195         * @param data
196         * @param slice an n-D slice
197         * @throws DatasetException 
198         */
199        public void setSlice(IDataset data, SliceND slice) throws DatasetException {
200                setSlice(null, data, slice);
201        }
202
203        @Override
204        public void setSlice(IMonitor monitor, IDataset data, int[] start, int[] stop, int[] step) throws DatasetException {
205                internalSetSlice(monitor, writeAsync, data, new SliceND(shape, maxShape, start, stop, step));
206        }
207
208        @Override
209        public void setSlice(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
210                internalSetSlice(monitor, writeAsync, data, slice);
211        }
212
213        @Override
214        public void setSliceSync(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
215                internalSetSlice(monitor, false, data, slice);
216        }
217
218        private void internalSetSlice(IMonitor monitor, final boolean async, IDataset data, SliceND slice) throws DatasetException {
219                int[] dshape = data instanceof Dataset ? ((Dataset) data).getShapeRef() : data.getShape();
220                if (dshape.length == 0) { // fix zero-rank case
221                        dshape = new int[] {1}; // FIXME remove
222                }
223                // if necessary, reshape the input data according to the shape of the slice
224                if (!Arrays.equals(slice.getShape(), dshape)) {
225                        data = data.getSliceView();
226                        data.setShape(slice.getShape());
227                }
228
229                SliceND nslice = calcTrueSlice(slice);
230                data = transformInput(data);
231
232                if (base != null) {
233                        ((ILazyWriteableDataset) base).setSlice(monitor, data, nslice);
234                } else {
235                        if (saver == null) {
236                                throw new DatasetException("Cannot write to file as saver not defined!");
237                        }
238
239                        try {
240                                if (async && saver instanceof ILazyAsyncSaver) {
241                                        ((ILazyAsyncSaver)saver).setSliceAsync(monitor, data, nslice);
242                                } else {
243                                        if (!saver.isFileWriteable()) {
244                                                throw new DatasetException("Cannot write to file as it is not writeable!");
245                                        }
246                                        saver.setSlice(monitor, data, nslice);
247                                }
248                        } catch (IOException e) {
249                                throw new DatasetException("Could not save dataset", e);
250                        }
251                        if (!refreshShape()) { // send event as data has changed
252                                eventDelegate.fire(new DataEvent(name, shape));
253                        }
254                }
255        }
256
257        /**
258         * Set saver (and also loader)
259         * @param saver
260         */
261        @Override
262        public void setSaver(ILazySaver saver) {
263                this.saver = saver;
264                this.loader = saver;
265        }
266
267        @Override
268        protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) {
269                if (base == null) {
270                        return new SliceND(oShape, maxShape, nstart, nstop, nstep);
271                }
272                return base.createSlice(nstart, nstop, nstep);
273        }
274
275        @Override
276        public Object getFillValue() {
277                return fillValue;
278        }
279
280        @Override
281        public void setFillValue(Object fill) {
282                fillValue = fill;
283        }
284}