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