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