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}