    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.getShape(),
    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 (!super.equals(obj)) {
    163                        return false;
    164                }
    165
    166                LazyWriteableDataset other = (LazyWriteableDataset) obj;
    167                if (!Arrays.equals(chunks, other.chunks)) {
    168                        return false;
    169                }
    170                if (fillValue == null) {
    171                        if (other.fillValue != null) {
    172                                return false;
    173                        }
    174                } else if (!fillValue.equals(other.fillValue)) {
    175                        return false;
    176                }
    177                if (saver == null) {
    178                        if (other.saver != null) {
    179                                return false;
    180                        }
    181                } else if (!saver.equals(other.saver)) {
    182                        return false;
    183                }
    184                if (writeAsync != other.writeAsync) {
    185                        return false;
    186                }
    187
    188                return true;
    189        }
    190
    191        @Override
    192        public int[] getChunking() {
    193                return chunks;
    194        }
    195
    196        @Override
    197        public void setChunking(int... chunks) {
    198                this.chunks = chunks == null ? null : chunks.clone();
    199        }
    200
    201        @Override
    202        public LazyWriteableDataset clone() {
    203                return new LazyWriteableDataset(this);
    204        }
    205
    206        @Override
    207        public LazyWriteableDataset getSliceView(int[] start, int[] stop, int[] step) {
    208                return (LazyWriteableDataset) super.getSliceView(start, stop, step);
    209        }
    210
    211        @Override
    212        public LazyWriteableDataset getSliceView(Slice... slice) {
    213                return (LazyWriteableDataset) super.getSliceView(slice);
    214        }
    215
    216        @Override
    217        public LazyWriteableDataset getSliceView(SliceND slice) {
    218                return (LazyWriteableDataset) super.getSliceView(slice);
    219        }
    220
    221        @Override
    222        public LazyWriteableDataset getTransposedView(int... axes) {
    223                return (LazyWriteableDataset) super.getTransposedView(axes);
    224        }
    225
    226        @Override
    227        public void setWritingAsync(boolean async) {
    228                writeAsync = async;
    229        }
    230
    231        /**
    232         * Set a slice of the dataset
    233         *
    234         * @param data
    235         * @param slice an n-D slice
    236         * @throws DatasetException
    237         */
    238        public void setSlice(IDataset data, SliceND slice) throws DatasetException {
    239                setSlice(null, data, slice);
    240        }
    241
    242        @Override
    243        public void setSlice(IMonitor monitor, IDataset data, int[] start, int[] stop, int[] step) throws DatasetException {
    244                internalSetSlice(monitor, writeAsync, data, new SliceND(shape, maxShape, start, stop, step));
    245        }
    246
    247        @Override
    248        public void setSlice(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
    249                internalSetSlice(monitor, writeAsync, data, slice);
    250        }
    251
    252        @Override
    253        public void setSliceSync(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
    254                internalSetSlice(monitor, false, data, slice);
    255        }
    256
    257        private void internalSetSlice(IMonitor monitor, final boolean async, IDataset data, SliceND slice) throws DatasetException {
    258                int[] dshape = data instanceof Dataset ? ((Dataset) data).getShapeRef() : data.getShape();
    259                if (dshape.length == 0) { // fix zero-rank case
    260                        dshape = new int[] {1}; // FIXME remove
    261                }
    262                // if necessary, reshape the input data according to the shape of the slice
    263                if (!Arrays.equals(slice.getShape(), dshape)) {
    264                        data = data.getSliceView();
    265                        data.setShape(slice.getShape());
    266                }
    267
    268                SliceND nslice = calcTrueSlice(slice);
    269                if (nslice == null) {
    270                        return; // nothing to set
    271                }
    272
    273                data = transformInput(data, slice);
    274
    275                if (saver == null) {
    276                        throw new DatasetException("Cannot write to file as saver not defined!");
    277                }
    278
    279                try {
    280                        if (async && saver instanceof ILazyAsyncSaver) {
    281                                ((ILazyAsyncSaver)saver).setSliceAsync(monitor, data, nslice);
    282                        } else {
    283                                if (!saver.isFileWriteable()) {
    284                                        throw new DatasetException("Cannot write to file as it is not writeable!");
    285                                }
    286                                saver.setSlice(monitor, data, nslice);
    287                        }
    288                } catch (IOException e) {
    289                        throw new DatasetException("Could not save dataset", e);
    290                }
    291                if (!refreshShape()) { // send event as data has changed
    292                        eventDelegate.fire(new DataEvent(name, shape));
    293                }
    294        }
    295
    296        /**
    297         * Set saver (and also loader)
    298         * @param saver
    299         */
    300        @Override
    301        public void setSaver(ILazySaver saver) {
    302                this.saver = saver;
    303                this.loader = saver;
    304        }
    305
    306        @Override
    307        protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) {
    308                return SliceND.createSlice(oShape, maxShape, nstart, nstop, nstep);
    309        }
    310
    311        @Override
    312        public Object getFillValue() {
    313                return fillValue;
    314        }
    315
    316        @Override
    317        public void setFillValue(Object fill) {
    318                fillValue = fill;
    319        }
    320}