    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.util.Arrays;
    013
    014import org.eclipse.january.io.ILazyDynamicLoader;
    015import org.eclipse.january.io.ILazyLoader;
    016
    017public class LazyDynamicDataset extends LazyDataset implements IDynamicDataset {
    018        private static final long serialVersionUID = -6296506563932840938L;
    019
    020        protected int[] maxShape;
    021
    022        protected transient DataListenerDelegate eventDelegate; // this does not need to be serialised!
    023
    024        protected IDatasetChangeChecker checker;
    025
    026        class PeriodicRunnable implements Runnable {
    027                long millis;
    028
    029                @Override
    030                public void run() {
    031                        while (true) {
    032                                try {
    033                                        Thread.sleep(millis);
    034                                } catch (InterruptedException e) {
    035                                        break;
    036                                }
    037                                if (checker == null || checker.check()) {
    038                                        fireDataListeners();
    039                                }
    040                        }
    041                }
    042        }
    043
    044        private transient PeriodicRunnable runner = new PeriodicRunnable();
    045
    046        private Thread checkingThread;
    047
    048        public LazyDynamicDataset(String name, int dtype, int elements, int[] shape, int[] maxShape, ILazyLoader loader) {
    049                super(name, dtype, elements, shape, loader);
    050                if (maxShape == null) {
    051                        this.maxShape = shape.clone();
    052                        // check there are no unlimited dimensions in shape
    053                        int rank = shape.length;
    054                        boolean isUnlimited = false;
    055                        for (int i = 0; i < rank; i++) {
    056                                if (shape[i] == ILazyWriteableDataset.UNLIMITED) {
    057                                        isUnlimited = true;
    058                                        break;
    059                                }
    060                        }
    061                        if (isUnlimited) { // set all zeros
    062                                for (int i = 0; i < rank; i++) {
    063                                        this.shape[i] = 0;
    064                                        this.oShape[i] = 0;
    065                                }
    066                        }
    067                } else {
    068                        this.maxShape = maxShape.clone();
    069                }
    070                this.eventDelegate = new DataListenerDelegate();
    071        }
    072
    073        /**
    074         * @since 2.2
    075         */
    076        protected LazyDynamicDataset(LazyDynamicDataset other) {
    077                super(other);
    078
    079                maxShape = other.maxShape;
    080                eventDelegate = other.eventDelegate;
    081                checker = other.checker;
    082                runner = other.runner;
    083        }
    084
    085        @Override
    086        public int hashCode() {
    087                final int prime = 31;
    088                int result = super.hashCode();
    089                result = prime * result + ((checker == null) ? 0 : checker.hashCode());
    090                result = prime * result + ((checkingThread == null) ? 0 : checkingThread.hashCode());
    091                result = prime * result + Arrays.hashCode(maxShape);
    092                return result;
    093        }
    094
    095        @Override
    096        public boolean equals(Object obj) {
    097                if (!super.equals(obj)) {
    098                        return false;
    099                }
    100
    101                LazyDynamicDataset other = (LazyDynamicDataset) obj;
    102                if (!Arrays.equals(maxShape, other.maxShape)) {
    103                        return false;
    104                }
    105
    106                if (checker == null) {
    107                        if (other.checker != null) {
    108                                return false;
    109                        }
    110                } else if (!checker.equals(other.checker)) {
    111                        return false;
    112                }
    113                if (checkingThread == null) {
    114                        if (other.checkingThread != null) {
    115                                return false;
    116                        }
    117                } else if (!checkingThread.equals(other.checkingThread)) {
    118                        return false;
    119                }
    120                return true;
    121        }
    122
    123        @Override
    124        public ILazyDataset getDataset() {
    125                return this;
    126        }
    127
    128        @Override
    129        public void addDataListener(IDataListener l) {
    130                eventDelegate.addDataListener(l);
    131        }
    132
    133        @Override
    134        public void removeDataListener(IDataListener l) {
    135                eventDelegate.removeDataListener(l);
    136        }
    137
    138        @Override
    139        public void fireDataListeners() {
    140                synchronized (eventDelegate) {
    141                        eventDelegate.fire(new DataEvent(name, shape));
    142                }
    143        }
    144
    145        @Override
    146        public boolean refreshShape() {
    147                if (loader instanceof ILazyDynamicLoader) {
    148                        return resize(((ILazyDynamicLoader)loader).refreshShape());
    149                }
    150                return false;
    151        }
    152
    153        @Override
    154        public boolean resize(int... newShape) {
    155                int rank = shape.length;
    156                if (newShape.length != rank) {
    157                        throw new IllegalArgumentException("Rank of new shape must match current shape");
    158                }
    159
    160                if (Arrays.equals(shape, newShape)) {
    161                        return false;
    162                }
    163
    164                if (maxShape != null) {
    165                        for (int i = 0; i < rank; i++) {
    166                                int m = maxShape[i];
    167                                if (m != -1 && newShape[i] > m) {
    168                                        throw new IllegalArgumentException("A dimension of new shape must not exceed maximum shape");
    169                                }
    170                        }
    171                }
    172                this.shape = newShape.clone();
    173                this.oShape = this.shape;
    174                try {
    175                        size = ShapeUtils.calcLongSize(shape);
    176                } catch (IllegalArgumentException e) {
    177                        size = Long.MAX_VALUE; // this indicates that the entire dataset cannot be read in!
    178                }
    179
    180                eventDelegate.fire(new DataEvent(name, shape));
    181                return true;
    182        }
    183
    184        @Override
    185        public int[] getMaxShape() {
    186                return maxShape;
    187        }
    188
    189        @Override
    190        public void setMaxShape(int... maxShape) {
    191                this.maxShape = maxShape == null ? shape.clone() : maxShape.clone();
    192
    193                if (this.maxShape.length > oShape.length) {
    194                        oShape = prependShapeWithOnes(this.maxShape.length, oShape);
    195                }
    196                if (this.maxShape.length > shape.length) {
    197                        shape = prependShapeWithOnes(this.maxShape.length, shape); // TODO this does not update any metadata
    198//                      setShapeInternal(prependShapeWithOnes(this.maxShape.length, shape));
    199                }
    200        }
    201
    202        private final static int[] prependShapeWithOnes(int rank, int[] shape) {
    203                int[] nShape = new int[rank];
    204                int excess = rank - shape.length;
    205                for (int i = 0; i < excess; i++) {
    206                        nShape[i] = 1;
    207                }
    208                for (int i = excess; i < nShape.length; i++) {
    209                        nShape[i] = shape[i - excess];
    210                }
    211                return nShape;
    212        }
    213
    214        @Override
    215        public LazyDynamicDataset clone() {
    216                return new LazyDynamicDataset(this);
    217        }
    218
    219        @Override
    220        public synchronized void startUpdateChecker(int milliseconds, IDatasetChangeChecker checker) {
    221                // stop any current checking threads
    222                if (checkingThread != null) {
    223                        checkingThread.interrupt();
    224                }
    225                this.checker = checker;
    226                if (checker != null) {
    227                        checker.setDataset(this);
    228                }
    229                if (milliseconds <= 0) {
    230                        return;
    231                }
    232
    233                runner.millis = milliseconds;
    234                checkingThread = new Thread(runner);
    235                checkingThread.setDaemon(true);
    236                checkingThread.setName("Checking thread with period " + milliseconds + "ms");
    237                checkingThread.start();
    238        }
    239}