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}