1// BEGIN LICENSE BLOCK 2// Version: CMPL 1.1 3// 4// The contents of this file are subject to the Cisco-style Mozilla Public 5// License Version 1.1 (the "License"); you may not use this file except 6// in compliance with the License. You may obtain a copy of the License 7// at www.eclipse-clp.org/license. 8// 9// Software distributed under the License is distributed on an "AS IS" 10// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11// the License for the specific language governing rights and limitations 12// under the License. 13// 14// The Original Code is The ECLiPSe Constraint Logic Programming System. 15// The Initial Developer of the Original Code is Cisco Systems, Inc. 16// Portions created by the Initial Developer are 17// Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18// 19// Contributor(s): 20// 21// END LICENSE BLOCK 22 23package com.parctechnologies.eclipse.visualisation; 24 25import java.util.*; 26import java.beans.*; 27import javax.swing.table.*; 28import javax.swing.*; 29import com.parctechnologies.eclipse.*; 30 31/** 32 * Basic implementation of the ViewletDataStore interface 33 */ 34public abstract class AbstractViewletDataStore 35 extends AbstractTableModel 36 implements ViewletDataStore 37{ 38 protected static final String fixedString = "fixed"; 39 protected static final String flexibleString = "flexible"; 40 41 // holds symref for this store 42 SymRef symRef; 43 44 45 // Handle to the viewable for which this is a store 46 protected Viewable viewable; 47 48 // used to store the size expanded *from* in between a startExpandDimension 49 // and a finishExpandDimension. 50 protected List oldSize; 51 52 /** 53 * Array of Lists of Strings. Each element in the array relates to a 54 * dimension. Each List is a list of strings: location names for the 55 * corresponding dimension. <p> 56 * locationNames[0] is a List of location names for dimension 1 in the 57 * viewable, 58 * locationNames[1] is for dimension 2 etc. 59 */ 60 protected List[] locationNames; 61 62 /** 63 * Atom which states whether the top level dimension is fixed or flexible. 64 */ 65 protected String topLevelFixity; 66 67 /** 68 * This is a list of Integers which lists the size of Elements recursively: 69 * i.e. if Elements contained 7 2-d ViewletArrays, each one with dimensions 70 * 4 x 8 then wholeSize = [7,4,8]. 71 */ 72 protected List wholeSize; 73 74 /** 75 * viewletFactory is used to create the new Viewlets which will populate the 76 * ViewletArray. 77 */ 78 protected ViewletFactory viewletFactory; 79 80 81 /** 82 * Holds the list of all listeners 83 */ 84 protected List listenerList; 85 86 /** 87 * Construct a ViewletArray given a list of integers (size), a fixity list 88 * (fixity) and a ViewletFactory to be used to populate it with Viewlets. 89 * 90 */ 91 public AbstractViewletDataStore(List size, 92 List fixity, 93 Viewable viewable, 94 ViewletFactory viewletFactory) 95 { 96 int topLevelSize = ((Integer) size.get(0)).intValue(); 97 this.wholeSize = new LinkedList(size); 98 this.topLevelFixity = (String) fixity.get(0); 99 this.viewable = viewable; 100 this.viewletFactory = viewletFactory; 101 this.listenerList = new LinkedList(); 102 locationNames = new List[getNDimensions()]; 103 } 104 105 /** 106 * dimNumber starts from 1. List should be a list of Strings. 107 */ 108 public void setLocationNames(int dimNumber, List locationNames) 109 { 110 this.locationNames[dimNumber - 1] = locationNames; 111 112 if (DebuggingSupport.logMessages) { 113 DebuggingSupport.logMessage(this, "Location names of dimension "+ 114 dimNumber+" set to "+locationNames); 115 } 116 117 } 118 119 /** 120 * dimNumber starts from 1. Fire propertyChange events to 121 * indicate that the store has changed shape */ 122 public void finishExpandDimension(int dimNumber) 123 { 124 125 if (DebuggingSupport.logMessages) { 126 DebuggingSupport.logMessage(this, "notifying property change listeners of expand"); 127 } 128 129 //propertyChangeSupport.firePropertyChange("arraySize", oldSize, wholeSize); 130 switch( dimNumber ) { 131 case 1 : { 132 int oldLimit = ((Integer)oldSize.get(0)).intValue(); 133 int newLimit = ((Integer)wholeSize.get(0)).intValue(); 134 fireTableRowsInserted(oldLimit,newLimit-1); 135 break; 136 } 137 case 2: { 138 // A new column has been added 139 //int oldLimit = ((Integer)oldSize.get(1)).intValue(); 140 //int newLimit = ((Integer)whole.get(1)).intValue(); 141 fireTableStructureChanged(); 142 //fireTableColumnsInserted(oldLimit,newLimit-1); 143 break; 144 } 145 default: { 146 //do nothing 147 //fireTableStructureChanged(); 148 } 149 } 150 } 151 152 /** 153 * dimNumber starts from 1. 154 */ 155 public List getLocationNames(int dimNumber) 156 { 157 return(locationNames[dimNumber - 1]); 158 } 159 160 /** 161 * dimNumber & locNumber start from 1. 162 */ 163 public String getLocationName(int dimNumber, int locNumber) 164 { 165 return((String) getLocationNames(dimNumber).get(locNumber - 1)); 166 } 167 168 /** 169 * Set the Java handle to the ECLiPSe side viewable 170 */ 171 public void setViewable(Viewable viewable) { 172 this.viewable = viewable; 173 } 174 175 /** 176 * Get the Java handle to the ECLiPSe side viewable 177 */ 178 public Viewable getViewable() { 179 return viewable; 180 } 181 182 /** 183 * Traverses the entire structure recursively to collect together all the 184 * viewlet indices. 185 */ 186 public ViewletRange getEntireViewletRange() { 187 // create an index representing the first element in the viewable 188 List start = new ArrayList(wholeSize.size()); 189 Integer one = new Integer(1); 190 for(int i = 0; i < wholeSize.size(); i++) { 191 start.add(one); 192 } 193 // createdElementIndices = allCombinations(size); 194 return createRange(start,wholeSize); 195 } 196 197 198 /** 199 * Returns a data iterator for the given ViewletRange. While the 200 * specified ViewletRange.iterator() will return indices, this 201 * Iterator will return copies of the ViewletData in the store. */ 202 public Iterator getViewletDataIterator(ViewletRange range) { 203 return new ViewletDataIterator(this, range); 204 } 205 206 207 protected static class ViewletDataIterator implements Iterator { 208 Iterator it; // store the underlying range iterator 209 ViewletDataStore store ; 210 public ViewletDataIterator(ViewletDataStore store, 211 ViewletRange range) { 212 this.it = range.iterator(); 213 this.store = store; 214 } 215 public boolean hasNext() { 216 return it.hasNext(); 217 } 218 public Object next() { 219 List index = (List)(it.next()); 220 if (DebuggingSupport.logMessages) { 221 DebuggingSupport.logMessage(this, "DataIterator index="+index); 222 } 223 return store.getViewletDataAt(index); 224 } 225 public void remove() { 226 it.remove(); 227 } 228 }; 229 230 231 232 public int getNDimensions() 233 { 234 return(getSize().size()); 235 } 236 237 public List getSize() 238 { 239 return(wholeSize); 240 } 241 242 /** 243 * Shrinks the ViewletArray to size newSize. Assumes the top-level fixity is 244 * flexible. 245 */ 246 public void shrinkTo(List newSize) 247 { 248 249 if (DebuggingSupport.logMessages) { 250 DebuggingSupport.logMessage(this, "shrinking to "+newSize); 251 } 252 253 254 if (DebuggingSupport.logMessages) { 255 DebuggingSupport.logMessage(this, "notifying property change listeners of shrink"); 256 } 257 } 258 259 /** 260 * Expand dimension nested at level <code>dimension</code> by one. 261 * records the pre-expansion size in the variable oldSize. 262 */ 263 public void startExpandDimension(int dimension) 264 { 265 Integer oldDimSize = (Integer) wholeSize.get(dimension-1); 266 Integer newDimSize = 267 new Integer(oldDimSize.intValue() + 1); 268 oldSize = new LinkedList(wholeSize); 269 wholeSize.set(dimension-1, newDimSize); 270 271 if (DebuggingSupport.logMessages) { 272 DebuggingSupport.logMessage(this, 273 "Viewlet array expanding to size:"+ 274 wholeSize); 275 } 276 } 277 278 /** 279 * Handy method to get the Viewlet at co-ordinates (row, column) in the case 280 * of a 2-d ViewletArray 281 */ 282 public ViewletData getViewletDataAt(int row, int column) 283 { 284 if(getSize().size() != 2) 285 { 286 return(null); 287 } 288 LinkedList index = new LinkedList(); 289 index.add(new Integer(row)); 290 index.add(new Integer(column)); 291 return(getViewletDataAt(index)); 292 } 293 294 /** 295 * Handy method to get the Viewlet at co-ordinate row in the case 296 * of a 1-d ViewletArray 297 */ 298 public ViewletData getViewletDataAt(int row) 299 { 300 if(getSize().size() != 1) 301 { 302 return(null); 303 } 304 LinkedList index = new LinkedList(); 305 index.add(new Integer(row)); 306 return(getViewletDataAt(index)); 307 } 308 309 /** 310 * This method must be overridden in sub-classes 311 */ 312 public abstract ViewletData getViewletDataAt(List index); 313 314 /** 315 * This method must be overridden in sub-classes 316 */ 317 public abstract void setViewletDataAt(List index, ViewletData data); 318 319 320 /** 321 * Fire data changed events upto any listeneing GUIs. 322 * 323 * <p>Default implemenation only fires table change events for 1 or 324 * two dimension viewables. */ 325 public void fireViewletRangeUpdated(ViewletRange range) { 326 SwingUtilities.invokeLater(new RangeUpdater(this,range)); 327 //new RangeUpdater(this,range).run(); 328 } 329 330 /** 331 * Adds a ViewletDataStoreListener 332 */ 333 public synchronized void addViewletDataStoreListener(ViewletDataStoreListener listener) { 334 listenerList.add(listener); 335 } 336 337 /** 338 * Removes a ViewletDataStoreListener 339 */ 340 public synchronized void removeViewletDataStoreListener(ViewletDataStoreListener listener) { 341 listenerList.remove(listener); 342 } 343 344 345 346 //-----------BEGIN: Implement the TableModel methods------------------ 347 348 /** 349 * Return number of rows 350 */ 351 public int getRowCount() { 352 int row = ((Integer)wholeSize.get(0)).intValue(); 353 return row; 354 } 355 /** 356 * Return number of columns 357 */ 358 public int getColumnCount() { 359 int col = 1; 360 if (wholeSize.size() > 1) { 361 col = ((Integer)wholeSize.get(1)).intValue(); 362 } 363 return col; 364 } 365 366 /** 367 * Return the viewlet at the given location. 368 * Note that the TableModel counts from 0, where as the Viewlet 369 * locations start from 1. 370 */ 371 public Object getValueAt(int row, int col) { 372 if (wholeSize.size() == 1) { 373 return getViewletDataAt(row+1); 374 } 375 return getViewletDataAt(row+1, col+1); 376 } 377 378 /** 379 * Returns the column name for the given column 380 */ 381 public String getColumnName(int column) { 382 if (wholeSize.size() == 1) { 383 return "1"; 384 } 385 return (String)(locationNames[1].get(column)); 386 } 387 388 /** 389 * Returns the column name for the given column 390 */ 391 public String getRowName(int row) { 392 return (String)(locationNames[0].get(row)); 393 } 394 395 //public Class getColumnClass(int c) {return getValueAt(0, c).getClass();} 396 public boolean isCellEditable(int row, int col) { 397 return false; 398 } 399 400 //-----------ENDOF: Implement the TableModel methods------------------ 401 402 /** 403 * Factory method for creating ViewletRange object from any given 404 * collection. 405 * 406 * <p>This defualt implementation will create a generic 407 * ViewletRange. Subclasses can override this if specialised 408 * structures make more sense. */ 409 public ViewletRange createRange(Collection indices) { 410 return new ViewletRangeCollection(indices); 411 } 412 413 414 /** 415 * Given a fromList and toList of integers returns a list of all 416 * lists X where X[i] is between fromList[i] and toList[i], 417 * inclusive. 418 * 419 * (e.g. for fromList = [1,1,1] toList = [3,2,2]) the method would 420 * return 421 * 422 * [1,1,1], [1,1,2], [1,2,1], [1,2,2], [2,1,1], ..., [3,2,1], 423 * [3,2,2]. */ 424 protected List allCombinations(List fromIndex, List toIndex) { 425 List result = new LinkedList(); 426 if(toIndex.size() > 0) 427 { 428 Integer fromHead = (Integer) fromIndex.get(0); 429 List fromTail = fromIndex.subList(1, fromIndex.size()); 430 Integer toHead = (Integer) toIndex.get(0); 431 List toTail = toIndex.subList(1, toIndex.size()); 432 List subCombs = allCombinations(fromTail, toTail); 433 Iterator subCombsIterator; 434 List currentComb; 435 for(int i = fromHead.intValue(); i <= toHead.intValue(); i++) 436 { 437 subCombsIterator = subCombs.iterator(); 438 while(subCombsIterator.hasNext()) 439 { 440 currentComb = new LinkedList(); 441 currentComb.add(new Integer(i)); 442 currentComb.addAll((List) subCombsIterator.next()); 443 result.add(currentComb); 444 } 445 } 446 } 447 else 448 { 449 result.add(new LinkedList()); 450 } 451 return(result); 452 } 453 454 /** 455 * Factory method for creating ViewletRange objects to cover all 456 * elements between the given start and end indices. 457 * 458 * <p>This defualt implementation will create a generic 459 * ViewletRange. Subclasses can override this if specialised 460 * structures make more sense. */ 461 public ViewletRange createRange(List fromIndex, List toIndex) { 462 return new ViewletRangeCollection(allCombinations(fromIndex, toIndex)); 463 } 464 465 protected class RangeUpdater implements Runnable { 466 ViewletRange range; 467 AbstractViewletDataStore store; 468 public RangeUpdater(AbstractViewletDataStore store, ViewletRange range) { 469 this.range = range; 470 this.store = store; 471 } 472 public void run() { 473 for(Iterator listenerIt =listenerList.iterator(); listenerIt.hasNext();) { 474 ((ViewletDataStoreListener)(listenerIt.next())).rangeUpdated(store,range); 475 } 476 if (range == null) { 477 store.fireTableDataChanged(); 478 return; 479 } 480 for(Iterator it = range.iterator(); 481 it.hasNext(); 482 ) { 483 List index = (List)it.next(); 484 if (index.size() == 1) { 485 int col = 0; 486 int row = ((Integer)index.get(0)).intValue()-1; 487 store.fireTableCellUpdated(row,col); 488 } else { 489 int row = ((Integer)index.get(0)).intValue()-1; 490 int col = ((Integer)index.get(1)).intValue()-1; 491 store.fireTableCellUpdated(row,col); 492 } 493 } 494 } 495 } 496 497 /** implments the symrefable interface*/ 498 public void setSymRef(SymRef symRef) { 499 this.symRef = symRef; 500 } 501 /** implments the symrefable interface*/ 502 public SymRef getSymRef() { 503 return this.symRef; 504 } 505 506} 507