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