    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        private boolean stop;
    026
    027        class PeriodicRunnable implements Runnable {
    028                long millis;
    029
    030                @Override
    031                public void run() {
    032                        while (!stop) {
    033                                try {
    034                                        Thread.sleep(millis);
    035                                } catch (InterruptedException e) {
    036                                        logger.error("Something has interrupted this periodic runner!");
    037                                        stop = true; // ends runner
    038                                }
    039                                if (checker == null || checker.check()) {
    040                                        fireDataListeners();
    041                                }
    042                        }
    043                }
    044        }
    045
    046        private transient PeriodicRunnable runner = new PeriodicRunnable();
    047
    048        private Thread checkingThread;
    049
    050        public LazyDynamicDataset(String name, int dtype, int elements, int[] shape, int[] maxShape, ILazyLoader loader) {
    051                super(name, dtype, elements, shape, loader);
    052                if (maxShape == null) {
    053                        this.maxShape = shape.clone();
    054                        // check there are no unlimited dimensions in shape
    055                        int rank = shape.length;
    056                        boolean isUnlimited = false;
    057                        for (int i = 0; i < rank; i++) {
    058                                if (shape[i] == ILazyWriteableDataset.UNLIMITED) {
    059                                        isUnlimited = true;
    060                                        break;
    061                                }
    062                        }
    063                        if (isUnlimited) { // set all zeros
    064                                for (int i = 0; i < rank; i++) {
    065                                        this.shape[i] = 0;
    066                                        this.oShape[i] = 0;
    067                                }
    068                        }
    069                } else {
    070                        this.maxShape = maxShape.clone();
    071                }
    072                this.eventDelegate = new DataListenerDelegate();
    073        }
    074
    075        @Override
    076        public ILazyDataset getDataset() {
    077                return this;
    078        }
    079
    080        @Override
    081        public void addDataListener(IDataListener l) {
    082                eventDelegate.addDataListener(l);
    083        }
    084
    085        @Override
    086        public void removeDataListener(IDataListener l) {
    087                eventDelegate.removeDataListener(l);
    088        }
    089
    090        @Override
    091        public void fireDataListeners() {
    092                synchronized (eventDelegate) {
    093                        eventDelegate.fire(new DataEvent(name, shape));
    094                }
    095        }
    096
    097        @Override
    098        public boolean refreshShape() {
    099                if (loader instanceof ILazyDynamicLoader) {
    100                        return resize(((ILazyDynamicLoader)loader).refreshShape());
    101                }
    102                return false;
    103        }
    104
    105        @Override
    106        public boolean resize(int... newShape) {
    107                if (base != null) {
    108                        throw new UnsupportedOperationException("Changing the shape of a view is not allowed");
    109                }
    110                int rank = shape.length;
    111                if (newShape.length != rank) {
    112                        throw new IllegalArgumentException("Rank of new shape must match current shape");
    113                }
    114
    115                if (Arrays.equals(shape, newShape)) {
    116                        return false;
    117                }
    118
    119                if (maxShape != null) {
    120                        for (int i = 0; i < rank; i++) {
    121                                int m = maxShape[i];
    122                                if (m != -1 && newShape[i] > m) {
    123                                        throw new IllegalArgumentException("A dimension of new shape must not exceed maximum shape");
    124                                }
    125                        }
    126                }
    127                this.shape = newShape.clone();
    128                this.oShape = this.shape;
    129                try {
    130                        size = ShapeUtils.calcLongSize(shape);
    131                } catch (IllegalArgumentException e) {
    132                        size = Long.MAX_VALUE; // this indicates that the entire dataset cannot be read in!
    133                }
    134
    135                eventDelegate.fire(new DataEvent(name, shape));
    136                return true;
    137        }
    138
    139        @Override
    140        public int[] getMaxShape() {
    141                return maxShape;
    142        }
    143
    144        @Override
    145        public void setMaxShape(int... maxShape) {
    146                this.maxShape = maxShape == null ? shape.clone() : maxShape.clone();
    147
    148                if (this.maxShape.length > oShape.length) {
    149                        oShape = prependShapeWithOnes(this.maxShape.length, oShape);
    150                }
    151                if (this.maxShape.length > shape.length) {
    152                        shape = prependShapeWithOnes(this.maxShape.length, shape); // TODO this does not update any metadata
    153//                      setShapeInternal(prependShapeWithOnes(this.maxShape.length, shape));
    154                }
    155        }
    156
    157        private final static int[] prependShapeWithOnes(int rank, int[] shape) {
    158                int[] nShape = new int[rank];
    159                int excess = rank - shape.length;
    160                for (int i = 0; i < excess; i++) {
    161                        nShape[i] = 1;
    162                }
    163                for (int i = excess; i < nShape.length; i++) {
    164                        nShape[i] = shape[i - excess];
    165                }
    166                return nShape;
    167        }
    168
    169        @Override
    170        public LazyDynamicDataset clone() {
    171                LazyDynamicDataset ret = new LazyDynamicDataset(new String(name), getDType(), getElementsPerItem(),
    172                                oShape, maxShape, loader);
    173                ret.shape = shape;
    174                ret.size = size;
    175                ret.prepShape = prepShape;
    176                ret.postShape = postShape;
    177                ret.begSlice = begSlice;
    178                ret.delSlice = delSlice;
    179                ret.map = map;
    180                ret.base = base;
    181                ret.metadata = copyMetadata();
    182                ret.oMetadata = oMetadata;
    183                ret.eventDelegate = eventDelegate;
    184                return ret;
    185        }
    186
    187        @Override
    188        public synchronized void startUpdateChecker(int milliseconds, IDatasetChangeChecker checker) {
    189                // stop any current checking threads
    190                if (checkingThread != null) {
    191                        stop = true;
    192                        if (checkingThread != null) {
    193                                checkingThread.interrupt();
    194                        }
    195                }
    196                this.checker = checker;
    197                if (checker != null) {
    198                        checker.setDataset(this);
    199                }
    200                if (milliseconds <= 0) {
    201                        return;
    202                }
    203
    204                runner.millis = milliseconds;
    205                checkingThread = new Thread(runner);
    206                checkingThread.setDaemon(true);
    207                checkingThread.setName("Checking thread with period " + milliseconds + "ms");
    208                checkingThread.start();
    209        }
    210}