    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}