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 protected int[] chunks; 022 023 protected transient DataListenerDelegate eventDelegate; // this does not need to be serialised! 024 025 protected IDatasetChangeChecker checker; 026 027 class PeriodicRunnable implements Runnable { 028 long millis; 029 030 @Override 031 public void run() { 032 while (true) { 033 try { 034 Thread.sleep(millis); 035 } catch (InterruptedException e) { 036 break; 037 } 038 if (checker == null || checker.check()) { 039 fireDataListeners(); 040 } 041 } 042 } 043 } 044 045 private transient PeriodicRunnable runner = new PeriodicRunnable(); 046 047 private Thread checkingThread; 048 049 /** 050 * Create a dynamic lazy dataset 051 * @param name of dataset 052 * @param dtype dataset type 053 * @param elements item size 054 * @param shape dataset shape 055 * @param maxShape maximum shape 056 * @param loader lazy loader 057 * @deprecated Use {@link #LazyDynamicDataset(ILazyLoader, String, int, Class, int[], int[])} 058 */ 059 @Deprecated 060 public LazyDynamicDataset(String name, int dtype, int elements, int[] shape, int[] maxShape, ILazyLoader loader) { 061 this(name, elements, DTypeUtils.getInterface(dtype), shape, maxShape, loader); 062 } 063 064 /** 065 * Create a dynamic lazy dataset 066 * @param name of dataset 067 * @param elements item size 068 * @param clazz dataset sub-interface 069 * @param shape dataset shape 070 * @param maxShape maximum shape 071 * @param loader lazy loader 072 * @since 2.3 073 * @deprecated Use {@link #LazyDynamicDataset(ILazyLoader, String, int, Class, int[], int[])} 074 */ 075 @Deprecated 076 public LazyDynamicDataset(String name, int elements, Class<? extends Dataset> clazz, int[] shape, int[] maxShape, ILazyLoader loader) { 077 this(loader, name, elements, clazz, shape, maxShape); 078 } 079 080 /** 081 * Create a dynamic lazy dataset 082 * @param loader lazy loader 083 * @param name of dataset 084 * @param elements item size 085 * @param clazz dataset sub-interface 086 * @param shape dataset shape 087 * @param maxShape maximum shape 088 * @since 2.3 089 */ 090 public LazyDynamicDataset(ILazyLoader loader, String name, int elements, Class<? extends Dataset> clazz, int[] shape, int[] maxShape) { 091 this(loader, name, elements, clazz, shape, maxShape, null); 092 } 093 094 /** 095 * Create a dynamic lazy dataset 096 * @param loader lazy loader 097 * @param name of dataset 098 * @param elements item size 099 * @param clazz dataset sub-interface 100 * @param shape dataset shape 101 * @param maxShape maximum shape 102 * @param chunks chunk shape 103 * @since 2.3 104 */ 105 public LazyDynamicDataset(ILazyLoader loader, String name, int elements, Class<? extends Dataset> clazz, int[] shape, int[] maxShape, int[] chunks) { 106 super(loader, name, elements, clazz, shape); 107 if (maxShape == null) { 108 this.maxShape = shape.clone(); 109 // check there are no unlimited dimensions in shape 110 int rank = shape.length; 111 boolean isUnlimited = false; 112 for (int i = 0; i < rank; i++) { 113 if (shape[i] == ILazyWriteableDataset.UNLIMITED) { 114 isUnlimited = true; 115 break; 116 } 117 } 118 if (isUnlimited) { // set all zeros 119 for (int i = 0; i < rank; i++) { 120 this.shape[i] = 0; 121 this.oShape[i] = 0; 122 } 123 } 124 } else { 125 this.maxShape = maxShape.clone(); 126 } 127 this.chunks = chunks == null ? null : chunks.clone(); 128 129 this.eventDelegate = new DataListenerDelegate(); 130 } 131 132 /** 133 * @param other dataset to clone 134 * @since 2.2 135 */ 136 protected LazyDynamicDataset(LazyDynamicDataset other) { 137 super(other); 138 139 maxShape = other.maxShape; 140 chunks = other.chunks; 141 eventDelegate = other.eventDelegate; 142 checker = other.checker; 143 runner = other.runner; 144 } 145 146 @Override 147 public int hashCode() { 148 final int prime = 31; 149 int result = super.hashCode(); 150 result = prime * result + ((checker == null) ? 0 : checker.hashCode()); 151 result = prime * result + ((checkingThread == null) ? 0 : checkingThread.hashCode()); 152 result = prime * result + Arrays.hashCode(maxShape); 153 result = prime * result + Arrays.hashCode(chunks); 154 return result; 155 } 156 157 @Override 158 public boolean equals(Object obj) { 159 if (this == obj) { 160 return true; 161 } 162 if (!super.equals(obj)) { 163 return false; 164 } 165 166 LazyDynamicDataset other = (LazyDynamicDataset) obj; 167 if (!Arrays.equals(maxShape, other.maxShape)) { 168 return false; 169 } 170 if (!Arrays.equals(chunks, other.chunks)) { 171 return false; 172 } 173 174 if (checker == null) { 175 if (other.checker != null) { 176 return false; 177 } 178 } else if (!checker.equals(other.checker)) { 179 return false; 180 } 181 if (checkingThread == null) { 182 if (other.checkingThread != null) { 183 return false; 184 } 185 } else if (!checkingThread.equals(other.checkingThread)) { 186 return false; 187 } 188 return true; 189 } 190 191 @Override 192 public ILazyDataset getDataset() { 193 return this; 194 } 195 196 @Override 197 public void addDataListener(IDataListener l) { 198 eventDelegate.addDataListener(l); 199 } 200 201 @Override 202 public void removeDataListener(IDataListener l) { 203 eventDelegate.removeDataListener(l); 204 } 205 206 @Override 207 public void fireDataListeners() { 208 synchronized (eventDelegate) { 209 eventDelegate.fire(new DataEvent(name, shape)); 210 } 211 } 212 213 @Override 214 public boolean refreshShape() { 215 if (loader instanceof ILazyDynamicLoader) { 216 return resize(((ILazyDynamicLoader)loader).refreshShape()); 217 } 218 return false; 219 } 220 221 @Override 222 public boolean resize(int... newShape) { 223 int rank = shape.length; 224 if (newShape.length != rank) { 225 throw new IllegalArgumentException("Rank of new shape must match current shape"); 226 } 227 228 if (Arrays.equals(shape, newShape)) { 229 return false; 230 } 231 232 if (maxShape != null) { 233 for (int i = 0; i < rank; i++) { 234 int m = maxShape[i]; 235 if (m != -1 && newShape[i] > m) { 236 throw new IllegalArgumentException("A dimension of new shape must not exceed maximum shape"); 237 } 238 } 239 } 240 this.shape = newShape.clone(); 241 this.oShape = this.shape; 242 try { 243 size = ShapeUtils.calcLongSize(shape); 244 } catch (IllegalArgumentException e) { 245 size = Long.MAX_VALUE; // this indicates that the entire dataset cannot be read in! 246 } 247 248 eventDelegate.fire(new DataEvent(name, shape)); 249 return true; 250 } 251 252 @Override 253 public int[] getMaxShape() { 254 return maxShape; 255 } 256 257 @Override 258 public void setMaxShape(int... maxShape) { 259 this.maxShape = maxShape == null ? shape.clone() : maxShape.clone(); 260 261 if (this.maxShape.length > oShape.length) { 262 oShape = prependShapeWithOnes(this.maxShape.length, oShape); 263 } 264 if (this.maxShape.length > shape.length) { 265 shape = prependShapeWithOnes(this.maxShape.length, shape); // TODO this does not update any metadata 266// setShapeInternal(prependShapeWithOnes(this.maxShape.length, shape)); 267 } 268 } 269 270 @Override 271 public int[] getChunking() { 272 return chunks; 273 } 274 275 @Override 276 public void setChunking(int... chunks) { 277 this.chunks = chunks == null ? null : chunks.clone(); 278 } 279 280 private final static int[] prependShapeWithOnes(int rank, int[] shape) { 281 int[] nShape = new int[rank]; 282 int excess = rank - shape.length; 283 for (int i = 0; i < excess; i++) { 284 nShape[i] = 1; 285 } 286 for (int i = excess; i < nShape.length; i++) { 287 nShape[i] = shape[i - excess]; 288 } 289 return nShape; 290 } 291 292 @Override 293 protected void checkSliceND(SliceND slice) { 294 slice.checkShapes(shape, maxShape); 295 } 296 297 @Override 298 protected SliceND createSlice(int[] nstart, int[] nstop, int[] nstep) { 299 return SliceND.createSlice(oShape, maxShape, nstart, nstop, nstep); 300 } 301 302 @Override 303 public LazyDynamicDataset clone() { 304 return new LazyDynamicDataset(this); 305 } 306 307 @Override 308 public synchronized void startUpdateChecker(int milliseconds, IDatasetChangeChecker checker) { 309 // stop any current checking threads 310 if (checkingThread != null) { 311 checkingThread.interrupt(); 312 } 313 this.checker = checker; 314 if (checker != null) { 315 checker.setDataset(this); 316 } 317 if (milliseconds <= 0) { 318 return; 319 } 320 321 runner.millis = milliseconds; 322 checkingThread = new Thread(runner); 323 checkingThread.setDaemon(true); 324 checkingThread.setName("Checking thread with period " + milliseconds + "ms"); 325 checkingThread.start(); 326 } 327}