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 (!super.equals(obj)) { 098 return false; 099 } 100 101 LazyDynamicDataset other = (LazyDynamicDataset) obj; 102 if (!Arrays.equals(maxShape, other.maxShape)) { 103 return false; 104 } 105 106 if (checker == null) { 107 if (other.checker != null) { 108 return false; 109 } 110 } else if (!checker.equals(other.checker)) { 111 return false; 112 } 113 if (checkingThread == null) { 114 if (other.checkingThread != null) { 115 return false; 116 } 117 } else if (!checkingThread.equals(other.checkingThread)) { 118 return false; 119 } 120 return true; 121 } 122 123 @Override 124 public ILazyDataset getDataset() { 125 return this; 126 } 127 128 @Override 129 public void addDataListener(IDataListener l) { 130 eventDelegate.addDataListener(l); 131 } 132 133 @Override 134 public void removeDataListener(IDataListener l) { 135 eventDelegate.removeDataListener(l); 136 } 137 138 @Override 139 public void fireDataListeners() { 140 synchronized (eventDelegate) { 141 eventDelegate.fire(new DataEvent(name, shape)); 142 } 143 } 144 145 @Override 146 public boolean refreshShape() { 147 if (loader instanceof ILazyDynamicLoader) { 148 return resize(((ILazyDynamicLoader)loader).refreshShape()); 149 } 150 return false; 151 } 152 153 @Override 154 public boolean resize(int... newShape) { 155 int rank = shape.length; 156 if (newShape.length != rank) { 157 throw new IllegalArgumentException("Rank of new shape must match current shape"); 158 } 159 160 if (Arrays.equals(shape, newShape)) { 161 return false; 162 } 163 164 if (maxShape != null) { 165 for (int i = 0; i < rank; i++) { 166 int m = maxShape[i]; 167 if (m != -1 && newShape[i] > m) { 168 throw new IllegalArgumentException("A dimension of new shape must not exceed maximum shape"); 169 } 170 } 171 } 172 this.shape = newShape.clone(); 173 this.oShape = this.shape; 174 try { 175 size = ShapeUtils.calcLongSize(shape); 176 } catch (IllegalArgumentException e) { 177 size = Long.MAX_VALUE; // this indicates that the entire dataset cannot be read in! 178 } 179 180 eventDelegate.fire(new DataEvent(name, shape)); 181 return true; 182 } 183 184 @Override 185 public int[] getMaxShape() { 186 return maxShape; 187 } 188 189 @Override 190 public void setMaxShape(int... maxShape) { 191 this.maxShape = maxShape == null ? shape.clone() : maxShape.clone(); 192 193 if (this.maxShape.length > oShape.length) { 194 oShape = prependShapeWithOnes(this.maxShape.length, oShape); 195 } 196 if (this.maxShape.length > shape.length) { 197 shape = prependShapeWithOnes(this.maxShape.length, shape); // TODO this does not update any metadata 198// setShapeInternal(prependShapeWithOnes(this.maxShape.length, shape)); 199 } 200 } 201 202 private final static int[] prependShapeWithOnes(int rank, int[] shape) { 203 int[] nShape = new int[rank]; 204 int excess = rank - shape.length; 205 for (int i = 0; i < excess; i++) { 206 nShape[i] = 1; 207 } 208 for (int i = excess; i < nShape.length; i++) { 209 nShape[i] = shape[i - excess]; 210 } 211 return nShape; 212 } 213 214 @Override 215 public LazyDynamicDataset clone() { 216 return new LazyDynamicDataset(this); 217 } 218 219 @Override 220 public synchronized void startUpdateChecker(int milliseconds, IDatasetChangeChecker checker) { 221 // stop any current checking threads 222 if (checkingThread != null) { 223 checkingThread.interrupt(); 224 } 225 this.checker = checker; 226 if (checker != null) { 227 checker.setDataset(this); 228 } 229 if (milliseconds <= 0) { 230 return; 231 } 232 233 runner.millis = milliseconds; 234 checkingThread = new Thread(runner); 235 checkingThread.setDaemon(true); 236 checkingThread.setName("Checking thread with period " + milliseconds + "ms"); 237 checkingThread.start(); 238 } 239}