    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}