    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 (this == obj) {
    098                        return true;
    099                }
    100                if (!super.equals(obj)) {
    101                        return false;
    102                }
    103
    104                LazyDynamicDataset other = (LazyDynamicDataset) obj;
    105                if (!Arrays.equals(maxShape, other.maxShape)) {
    106                        return false;
    107                }
    108
    109                if (checker == null) {
    110                        if (other.checker != null) {
    111                                return false;
    112                        }
    113                } else if (!checker.equals(other.checker)) {
    114                        return false;
    115                }
    116                if (checkingThread == null) {
    117                        if (other.checkingThread != null) {
    118                                return false;
    119                        }
    120                } else if (!checkingThread.equals(other.checkingThread)) {
    121                        return false;
    122                }
    123                return true;
    124        }
    125
    126        @Override
    127        public ILazyDataset getDataset() {
    128                return this;
    129        }
    130
    131        @Override
    132        public void addDataListener(IDataListener l) {
    133                eventDelegate.addDataListener(l);
    134        }
    135
    136        @Override
    137        public void removeDataListener(IDataListener l) {
    138                eventDelegate.removeDataListener(l);
    139        }
    140
    141        @Override
    142        public void fireDataListeners() {
    143                synchronized (eventDelegate) {
    144                        eventDelegate.fire(new DataEvent(name, shape));
    145                }
    146        }
    147
    148        @Override
    149        public boolean refreshShape() {
    150                if (loader instanceof ILazyDynamicLoader) {
    151                        return resize(((ILazyDynamicLoader)loader).refreshShape());
    152                }
    153                return false;
    154        }
    155
    156        @Override
    157        public boolean resize(int... newShape) {
    158                int rank = shape.length;
    159                if (newShape.length != rank) {
    160                        throw new IllegalArgumentException("Rank of new shape must match current shape");
    161                }
    162
    163                if (Arrays.equals(shape, newShape)) {
    164                        return false;
    165                }
    166
    167                if (maxShape != null) {
    168                        for (int i = 0; i < rank; i++) {
    169                                int m = maxShape[i];
    170                                if (m != -1 && newShape[i] > m) {
    171                                        throw new IllegalArgumentException("A dimension of new shape must not exceed maximum shape");
    172                                }
    173                        }
    174                }
    175                this.shape = newShape.clone();
    176                this.oShape = this.shape;
    177                try {
    178                        size = ShapeUtils.calcLongSize(shape);
    179                } catch (IllegalArgumentException e) {
    180                        size = Long.MAX_VALUE; // this indicates that the entire dataset cannot be read in!
    181                }
    182
    183                eventDelegate.fire(new DataEvent(name, shape));
    184                return true;
    185        }
    186
    187        @Override
    188        public int[] getMaxShape() {
    189                return maxShape;
    190        }
    191
    192        @Override
    193        public void setMaxShape(int... maxShape) {
    194                this.maxShape = maxShape == null ? shape.clone() : maxShape.clone();
    195
    196                if (this.maxShape.length > oShape.length) {
    197                        oShape = prependShapeWithOnes(this.maxShape.length, oShape);
    198                }
    199                if (this.maxShape.length > shape.length) {
    200                        shape = prependShapeWithOnes(this.maxShape.length, shape); // TODO this does not update any metadata
    201//                      setShapeInternal(prependShapeWithOnes(this.maxShape.length, shape));
    202                }
    203        }
    204
    205        private final static int[] prependShapeWithOnes(int rank, int[] shape) {
    206                int[] nShape = new int[rank];
    207                int excess = rank - shape.length;
    208                for (int i = 0; i < excess; i++) {
    209                        nShape[i] = 1;
    210                }
    211                for (int i = excess; i < nShape.length; i++) {
    212                        nShape[i] = shape[i - excess];
    213                }
    214                return nShape;
    215        }
    216
    217        @Override
    218        public LazyDynamicDataset clone() {
    219                return new LazyDynamicDataset(this);
    220        }
    221
    222        @Override
    223        public synchronized void startUpdateChecker(int milliseconds, IDatasetChangeChecker checker) {
    224                // stop any current checking threads
    225                if (checkingThread != null) {
    226                        checkingThread.interrupt();
    227                }
    228                this.checker = checker;
    229                if (checker != null) {
    230                        checker.setDataset(this);
    231                }
    232                if (milliseconds <= 0) {
    233                        return;
    234                }
    235
    236                runner.millis = milliseconds;
    237                checkingThread = new Thread(runner);
    238                checkingThread.setDaemon(true);
    239                checkingThread.setName("Checking thread with period " + milliseconds + "ms");
    240                checkingThread.start();
    241        }
    242}