    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 of dataset
    033         * @param dtype dataset type
    034         * @param elements item size
    035         * @param shape dataset shape
    036         * @param maxShape maximum shape
    037         * @param chunks chunk shape
    038         * @param saver lazy saver
    039         * @deprecated Use {@link #LazyWriteableDataset(ILazySaver, String, int, Class, int[], int[], int[])}
    040         */
    041        @Deprecated
    042        public LazyWriteableDataset(String name, int dtype, int elements, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
    043                this(saver, name, elements, DTypeUtils.getInterface(dtype), shape, maxShape, chunks);
    044        }
    045
    046        /**
    047         * Create a lazy dataset
    048         * @param saver lazy saver
    049         * @param name of dataset
    050         * @param clazz dataset sub-interface
    051         * @param shape dataset shape
    052         * @param maxShape maximum shape
    053         * @param chunks chunk shape
    054         * @since 2.3
    055         */
    056        public LazyWriteableDataset(ILazySaver saver, String name, Class<? extends Dataset> clazz, int[] shape, int[] maxShape, int[] chunks) {
    057                this(saver, name, 1, clazz, shape, maxShape, chunks);
    058        }
    059
    060        /**
    061         * Create a lazy dataset
    062         * @param saver lazy saver
    063         * @param name of dataset
    064         * @param elements item size
    065         * @param clazz dataset sub-interface
    066         * @param shape dataset shape
    067         * @param maxShape maximum shape
    068         * @param chunks chunk shape
    069         * @since 2.3
    070         */
    071        public LazyWriteableDataset(ILazySaver saver, String name, int elements, Class<? extends Dataset> clazz, int[] shape, int[] maxShape, int[] chunks) {
    072                super(saver, name, elements, clazz, shape, maxShape);
    073                this.chunks = chunks == null ? null : chunks.clone();
    074                this.saver = saver;
    075
    076                size = ShapeUtils.calcLongSize(this.shape);
    077        }
    078
    079        /**
    080         * Create a lazy dataset
    081         * @param name of dataset
    082         * @param dtype dataset type
    083         * @param shape dataset shape
    084         * @param maxShape maximum shape
    085         * @param chunks chunk shape
    086         * @param saver lazy saver
    087         * @deprecated Use {@link #LazyWriteableDataset(ILazySaver, String, int, Class, int[], int[], int[])}
    088         */
    089        @Deprecated
    090        public LazyWriteableDataset(String name, int dtype, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
    091                this(name, dtype, 1, shape, maxShape, chunks, saver);
    092        }
    093
    094        /**
    095         * Create a lazy dataset using element class
    096         * @param name of dataset
    097         * @param eClass element class
    098         * @param elements item size
    099         * @param shape dataset shape
    100         * @param maxShape maximum shape
    101         * @param chunks chunk shape
    102         * @param saver lazy saver
    103         */
    104        public LazyWriteableDataset(String name, Class<?> eClass, int elements, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
    105                this(saver, name, elements, InterfaceUtils.getInterfaceFromClass(elements, eClass), shape, maxShape, chunks);
    106        }
    107
    108        /**
    109         * Create a lazy dataset using element class
    110         * @param name of dataset
    111         * @param eClass element class
    112         * @param shape dataset shape
    113         * @param maxShape maximum shape
    114         * @param chunks chunk shape
    115         * @param saver lazy saver
    116         */
    117        public LazyWriteableDataset(String name, Class<?> eClass, int[] shape, int[] maxShape, int[] chunks, ILazySaver saver) {
    118                this(saver, name, 1, InterfaceUtils.getInterfaceFromClass(1, eClass), shape, maxShape, chunks);
    119        }
    120
    121        /**
    122         * @param other dataset
    123         * @since 2.2
    124         */
    125        protected LazyWriteableDataset(LazyWriteableDataset other) {
    126                super(other);
    127
    128                chunks = other.chunks;
    129                saver  = other.saver;
    130                fillValue  = other.fillValue;
    131                writeAsync = other.writeAsync;
    132        }
    133
    134        /**
    135         * Create a lazy writeable dataset based on in-memory data (handy for testing)
    136         * @param dataset input
    137         * @return lazy writeable dataset
    138         */
    139        public static LazyWriteableDataset createLazyDataset(final Dataset dataset) {
    140                return createLazyDataset(dataset, null);
    141        }
    142
    143        /**
    144         * Create a lazy writeable dataset based on in-memory data (handy for testing)
    145         * @param dataset input
    146         * @param maxShape maximum shape
    147         * @return lazy writeable dataset
    148         */
    149        public static LazyWriteableDataset createLazyDataset(final Dataset dataset, final int[] maxShape) {
    150                return new LazyWriteableDataset(new ILazySaver() {
    151                        private static final long serialVersionUID = ILazySaver.serialVersionUID;
    152
    153                        Dataset d = dataset;
    154                        @Override
    155                        public boolean isFileReadable() {
    156                                return true;
    157                        }
    158
    159                        @Override
    160                        public boolean isFileWriteable() {
    161                                return true;
    162                        }
    163
    164                        @Override
    165                        public void initialize() throws IOException {
    166                        }
    167
    168                        @Override
    169                        public Dataset getDataset(IMonitor mon, SliceND slice) throws IOException {
    170                                return d.getSlice(mon, slice);
    171                        }
    172
    173                        @Override
    174                        public void setSlice(IMonitor mon, IDataset data, SliceND slice) throws IOException {
    175                                if (slice.isExpanded()) {
    176                                        Dataset od = d;
    177                                        d = DatasetFactory.zeros(od.getClass(), slice.getSourceShape());
    178                                        d.setSlice(od, SliceND.createSlice(d, null, od.getShapeRef()));
    179                                }
    180                                d.setSlice(data, slice);
    181                        }
    182                },
    183                dataset.getName(), dataset.getElementsPerItem(), dataset.getClass(), dataset.getShapeRef(), maxShape, null);
    184        }
    185
    186        @Override
    187        public int hashCode() {
    188                final int prime = 31;
    189                int result = super.hashCode();
    190                result = prime * result + Arrays.hashCode(chunks);
    191                result = prime * result + ((fillValue == null) ? 0 : fillValue.hashCode());
    192                result = prime * result + (writeAsync ? 1231 : 1237);
    193                return result;
    194        }
    195
    196        @Override
    197        public boolean equals(Object obj) {
    198                if (this == obj) {
    199                        return true;
    200                }
    201                if (!super.equals(obj)) {
    202                        return false;
    203                }
    204
    205                LazyWriteableDataset other = (LazyWriteableDataset) obj;
    206                if (!Arrays.equals(chunks, other.chunks)) {
    207                        return false;
    208                }
    209                if (fillValue == null) {
    210                        if (other.fillValue != null) {
    211                                return false;
    212                        }
    213                } else if (!fillValue.equals(other.fillValue)) {
    214                        return false;
    215                }
    216                if (saver == null) {
    217                        if (other.saver != null) {
    218                                return false;
    219                        }
    220                } else if (!saver.equals(other.saver)) {
    221                        return false;
    222                }
    223                if (writeAsync != other.writeAsync) {
    224                        return false;
    225                }
    226
    227                return true;
    228        }
    229
    230        @Override
    231        public int[] getChunking() {
    232                return chunks;
    233        }
    234
    235        @Override
    236        public void setChunking(int... chunks) {
    237                this.chunks = chunks == null ? null : chunks.clone();
    238        }
    239
    240        @Override
    241        public LazyWriteableDataset clone() {
    242                return new LazyWriteableDataset(this);
    243        }
    244
    245        @Override
    246        public LazyWriteableDataset getSliceView(int[] start, int[] stop, int[] step) {
    247                return (LazyWriteableDataset) super.getSliceView(start, stop, step);
    248        }
    249
    250        @Override
    251        public LazyWriteableDataset getSliceView(Slice... slice) {
    252                return (LazyWriteableDataset) super.getSliceView(slice);
    253        }
    254
    255        @Override
    256        public LazyWriteableDataset getSliceView(SliceND slice) {
    257                return (LazyWriteableDataset) super.getSliceView(slice);
    258        }
    259
    260        @Override
    261        public LazyWriteableDataset getTransposedView(int... axes) {
    262                return (LazyWriteableDataset) super.getTransposedView(axes);
    263        }
    264
    265        @Override
    266        public void setWritingAsync(boolean async) {
    267                writeAsync = async;
    268        }
    269
    270        /**
    271         * Set a slice of the dataset
    272         *
    273         * @param data to set
    274         * @param slice an n-D slice
    275         * @throws DatasetException when cannot write data
    276         */
    277        public void setSlice(IDataset data, SliceND slice) throws DatasetException {
    278                setSlice(null, data, slice);
    279        }
    280
    281        @Override
    282        public void setSlice(IMonitor monitor, IDataset data, int[] start, int[] stop, int[] step) throws DatasetException {
    283                internalSetSlice(monitor, writeAsync, data, new SliceND(shape, maxShape, start, stop, step));
    284        }
    285
    286        @Override
    287        public void setSlice(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
    288                checkSliceND(slice);
    289                internalSetSlice(monitor, writeAsync, data, slice);
    290        }
    291
    292        @Override
    293        public void setSliceSync(IMonitor monitor, IDataset data, SliceND slice) throws DatasetException {
    294                checkSliceND(slice);
    295                internalSetSlice(monitor, false, data, slice);
    296        }
    297
    298        private void internalSetSlice(IMonitor monitor, final boolean async, IDataset data, SliceND slice) throws DatasetException {
    299                if (slice != null) {
    300                        int[] dshape = data instanceof Dataset ? ((Dataset) data).getShapeRef() : data.getShape();
    301
    302                        // if necessary, reshape the input data according to the shape of the slice
    303                        if (!Arrays.equals(slice.getShape(), dshape)) {
    304                                data = data.getSliceView();
    305                                data.setShape(slice.getShape());
    306                        }
    307                }
    308
    309                SliceND nslice = calcTrueSlice(slice);
    310                if (nslice == null) {
    311                        return; // nothing to set
    312                }
    313
    314                data = transformInput(data, slice);
    315
    316                if (saver == null) {
    317                        throw new DatasetException("Cannot write to file as saver not defined!");
    318                }
    319
    320                try {
    321                        if (async && saver instanceof ILazyAsyncSaver) {
    322                                ((ILazyAsyncSaver)saver).setSliceAsync(monitor, data, nslice);
    323                        } else {
    324                                if (!saver.isFileWriteable()) {
    325                                        throw new DatasetException("Cannot write to file as it is not writeable!");
    326                                }
    327                                saver.setSlice(monitor, data, nslice);
    328                        }
    329                } catch (IOException e) {
    330                        throw new DatasetException("Could not save dataset", e);
    331                }
    332                if (!refreshShape()) { // send event as data has changed
    333                        eventDelegate.fire(new DataEvent(name, shape));
    334                }
    335        }
    336
    337        /**
    338         * Set saver (and also loader)
    339         * @param saver lazy saver
    340         */
    341        @Override
    342        public void setSaver(ILazySaver saver) {
    343                this.saver = saver;
    344                this.loader = saver;
    345        }
    346
    347        @Override
    348        protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) {
    349                return SliceND.createSlice(oShape, maxShape, nstart, nstop, nstep);
    350        }
    351
    352        @Override
    353        public Object getFillValue() {
    354                return fillValue;
    355        }
    356
    357        @Override
    358        public void setFillValue(Object fill) {
    359                fillValue = fill;
    360        }
    361
    362        @Override
    363        public LazyWriteableDataset squeezeEnds() {
    364                return (LazyWriteableDataset) super.squeezeEnds();
    365        }
    366}