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 com.parctechnologies.eclipse.*; 29 30/** 31 * A ViewletArray is a recursive data structure intended to mirror the number, 32 * size and fixity of the dimensions of a viewable on the eclipse side. 33 * 34 * Instead of viewable elements, it contains viewlets. Each viewlet is produced 35 * from a ViewletFactory. 36 * <p> 37 * Note that ViewletArray is not responsible for maintaining location names, 38 * only for keeping references to them and providing access to them. 39 */ 40public class ViewletArray extends AbstractViewletDataStore implements MultiViewletDataStore 41{ 42 /** 43 * This is a list of Integers which denotes this ViewletArray's location 44 * within the outermost ViewletArray. E.g. a top-level ViewletArray with 45 * size [4,3] would have location [], and its elements would have location 46 * [1], [2], [3], [4] and the elments of the first of these would have 47 * locations [1,1], [1,2] and [1,3]. 48 */ 49 private List location; 50 51 /** 52 * subFixity lists the fixities of Elements recursively. So a ViewletArray 53 * which mirrored a viewable with fixity [fixed, fixed, flexible] would have 54 * subFixity [fixed, flexible]. 55 * 56 * */ 57 private List subFixity; 58 59 /** 60 * elements is an instance of inner class Elements. If this ViewletArray is 61 * one-dimensional, the contents of elements are the viewlets of the 62 * ViewletArray. If this ViewletArray is multi-dimensional, then the contents 63 * of elements are the sub-ViewletArrays. 64 * 65 */ 66 private Elements elements; 67 68 /** 69 *Hold the individual ViewletDataStores on a per type basis 70 */ 71 private Map wrappedStoreMap; 72 73 /** 74 * Construct a ViewletArray given a list of integers (size), a fixity list 75 * (fixity) and a ViewletFactory to be used to populate it with Viewlets. 76 * 77 */ 78 public ViewletArray(List size, 79 List fixity, 80 Viewable viewable, 81 ViewletFactory viewletFactory) 82 { 83 this(size, fixity, viewable, viewletFactory, Collections.EMPTY_LIST); 84 } 85 86 87 /** 88 * Construct a ViewletArray given a list of integers (size), a fixity list 89 * (fixity), a ViewletFactory to be used to populate it with Viewlets and a 90 * list of integers to be the top level location (location). 91 * The sub-ViewletArrays will then have locations location @ [1], location @ 92 * [2] etc. (where @ means append). 93 * <p> 94 * After setting up a few instance members based on the constructor's 95 * parameters, we initialise the elements member by inspecting the fixity of 96 * the top-level dimension. If it is 'fixed' we use FixedElements, and if it 97 * is 'flexible' we use FlexibleElements. These are both subclasses of 98 * Elements: FixedElements is optimized for fast access whereas 99 * FlexibleElements allows expansion. 100 * <p> 101 * To populate elements, we use createSubArray which will either fill it with 102 * Viewlets (in the one-dimensional case) or ViewletArrays in the 103 * multi-dimensional case. So, the ViewletArray constructor is mutually 104 * recursive with this static method. 105 * 106 */ 107 private ViewletArray(List size, List fixity, Viewable viewable, 108 ViewletFactory viewletFactory, 109 List location) 110 { 111 super(size, fixity, viewable, viewletFactory); 112 int topLevelSize = ((Integer) size.get(0)).intValue(); 113 this.wholeSize = new LinkedList(size); 114 this.topLevelFixity = (String) fixity.get(0); 115 List subSize = size.subList(1, size.size()); 116 subFixity = fixity.subList(1, fixity.size()); 117 this.viewletFactory = viewletFactory; 118 this.location = location; 119 if(topLevelFixity.equals(fixedString)) 120 { 121 elements = new FixedElements(topLevelSize); 122 } 123 else 124 { 125 elements = new FlexibleElements(topLevelSize); 126 } 127 List subLocation; 128 for(int i = 0; i < topLevelSize; i++) 129 { 130 subLocation = new LinkedList(location); 131 subLocation.add(new Integer(i+1)); 132 elements.set(i, createSubArray(subSize, subFixity, getViewable(), viewletFactory, subLocation)); 133 } 134 } 135 136 /** 137 * dimNumber starts from 1. List should be a list of Strings. 138 */ 139 public void setLocationNames(int dimNumber, List locationNames) 140 { 141 super.setLocationNames(dimNumber, locationNames); 142 } 143 144 /** 145 * dimNumber starts from 1. 146 */ 147 public void finishExpandDimension(int dimNumber) 148 { 149 super.finishExpandDimension(dimNumber); 150 } 151 152 /** 153 * Traverses the entire structure recursively to collect together all the 154 * element indices. 155 */ 156 public ViewletRange getEntireViewletRange() 157 { 158 return super.getEntireViewletRange(); 159// List elementsList = elements.toList(); 160// if(wholeSize.size() == 1) 161// { 162// ViewletRange res = new ViewletRangeCollection(); 163// res.addAll(elementsList); 164// return(res); 165// } 166// Iterator elementsIterator = elementsList.iterator(); 167// ViewletArray viewletArray; 168// ViewletRange result = new ViewletRangeCollection(); 169// while(elementsIterator.hasNext()) 170// { 171// viewletArray = (ViewletArray) elementsIterator.next(); 172// result.addAll(viewletArray.getAllViewletData()); 173// } 174// return(result); 175 } 176 177 /** 178 * Returns an Object to be inserted into the elements member of a 179 * ViewletArray. This method is mutually recursive with the ViewletArray 180 * constructor.<p> 181 * If size isEmpty then the elements object being populated is one-dimensional 182 * and so the method uses the ViewletFactory parameter to build a Viewlet and 183 * returns the Viewlet. The newViewlet's elementReference is set based on 184 * its location within the top level ViewletArray. <p> 185 * Otherwise, the elements object being populated is multi-dimensional and so 186 * the returned object is a ViewletArray, returned by the constructor. 187 * 188 */ 189 private static Object createSubArray(List size, List fixity, 190 Viewable viewable, 191 ViewletFactory viewletFactory, 192 List location) 193 { 194 if(size.isEmpty()) 195 { 196 197 if (DebuggingSupport.logMessages) { 198 DebuggingSupport.logMessage(null, 199 "building viewlet with location: "+ 200 location); 201 } 202 203 ViewletData newViewlet = viewletFactory.build(); 204 return(newViewlet); 205 } 206 else 207 { 208 return(new ViewletArray(size, fixity, viewable, viewletFactory, 209 location)); 210 } 211 } 212 213 /** 214 * Shrinks the ViewletArray to size newSize. Assumes the top-level fixity is 215 * flexible. 216 */ 217 public void shrinkTo(List newSize) 218 { 219 super.shrinkTo(newSize); 220 if(wholeSize.equals(newSize)) // no change necessary 221 { 222 return; 223 } 224 // First shrink the top level dimension. 225 int newTopLevelSize = ((Integer) newSize.get(0)).intValue(); 226 elements.shrinkTo(newTopLevelSize); 227 if(wholeSize.size() > 1) // recursion necessary 228 { 229 // Cast each element to a Viewlet array and shrink recursively. 230 List subNewSize = newSize.subList(1, newSize.size()); 231 for(int i = 0; i < elements.size(); i++) 232 { 233 ((ViewletArray) elements.get(i)).shrinkTo(subNewSize); 234 } 235 } 236 wholeSize = new LinkedList(newSize); 237 // Indicate to the table that rows/columns have been removed 238 fireTableStructureChanged(); 239 } 240 241 /** 242 * Expand dimension nested at level <code>dimension</code> by one. If the 243 * parameter dimension is greater than 1 this means that this.elements stays 244 * the same size and it is the sub-ViewletArrays which expand. Otherwise, we 245 * have to expand the top level by adding a new object to elements. This is 246 * created using createSubArray. 247 */ 248 public void startExpandDimension(int dimension) 249 { 250 super.startExpandDimension(dimension); 251 Integer oldDimSize = (Integer) wholeSize.get(dimension-1); 252 Integer newDimSize = 253 new Integer(oldDimSize.intValue() + 1); 254 if(dimension > 1) 255 { 256 for(int i = 0; i < elements.size(); i++) 257 { 258 ((ViewletArray) elements.get(i)).startExpandDimension(dimension - 1); 259 } 260 } 261 else 262 { 263 List subSize = wholeSize.subList(1, wholeSize.size()); 264 List subLocation = new LinkedList(location); 265 subLocation.add(newDimSize); 266 elements.expand(createSubArray(subSize, subFixity, 267 getViewable(), 268 viewletFactory, subLocation)); 269 } 270 } 271 272 273 /** 274 * Implements the abstract getViewletDataAt method 275 */ 276 public ViewletData getViewletDataAt(List index) 277 { 278 if(index.size() != getSize().size()) 279 { 280 throw(new IllegalArgumentException()); 281 } 282 return((ViewletData) getElement(index)); 283 } 284 285 /** 286 * Implements the abstract setViewletDataAt method 287 */ 288 public void setViewletDataAt(List index, ViewletData data) 289 { 290 if(index.size() != getSize().size()) 291 { 292 throw(new IllegalArgumentException()); 293 } 294 setElement(index, data); 295 } 296 297 /** 298 * Get element with location index within the ViewletArray. Result may be a 299 * ViewletArray or a Viewlet, depending on the length of the index parameter. 300 */ 301 private Object getElement(List index) 302 { 303 Object sub = 304 elements.get(((Integer) index.get(0)).intValue() - 1); 305 if(index.size() == 1) 306 { 307 return(sub); 308 } 309 List subIndex = index.subList(1, index.size()); 310 return(((ViewletArray) sub).getElement(subIndex)); 311 } 312 313 /** 314 * Sets the element with location index within the ViewletArray. */ 315 private void setElement(List index, Object data) 316 { 317 if (index.size() == 1) { 318 elements.set(((Integer) index.get(0)).intValue() - 1, data); 319 } else { 320 Object sub = 321 elements.get(((Integer) index.get(0)).intValue() - 1); 322 List subIndex = index.subList(1, index.size()); 323 ((ViewletArray) sub).setElement(subIndex, data); 324 } 325 } 326 327 /** 328 * Abstract superclass of the two classes which the elements member may 329 * instantiate. 330 */ 331 private abstract class Elements 332 { 333 abstract Object get(int index); 334 abstract void set(int index, Object element); 335 void expand(Object newElement){} 336 void shrinkTo(int newSize){} 337 abstract int size(); 338 abstract List toList(); 339 } 340 341 /** 342 * FixedElements is basically just a bridge between an Object array and 343 * Elements 344 */ 345 private class FixedElements extends Elements 346 { 347 private Object[] elements; 348 FixedElements(int size) 349 { 350 elements = new Object[size]; 351 } 352 Object get(int index) 353 { 354 return(elements[index]); 355 } 356 void set(int index, Object element) 357 { 358 elements[index] = element; 359 } 360 int size() 361 { 362 return(elements.length); 363 } 364 List toList() 365 { 366 LinkedList result = new LinkedList(); 367 for(int i = 0; i < elements.length; i++) 368 { 369 result.add(elements[i]); 370 } 371 return(result); 372 } 373 } 374 375 /** 376 * FlexibleElements is basically just a bridge between a Vector and 377 * Elements. If there is a better class which supports expanding and 378 * shrinking (maybe LinkedList?), this could be substituted for Vector. 379 */ 380 private class FlexibleElements extends Elements 381 { 382 private Vector elements; 383 FlexibleElements(int size) 384 { 385 elements = new Vector(size); 386 elements.setSize(size); 387 } 388 Object get(int index) 389 { 390 return(elements.get(index)); 391 } 392 void set(int index, Object element) 393 { 394 elements.set(index, element); 395 } 396 int size() 397 { 398 return(elements.size()); 399 } 400 void expand(Object newElement) 401 { 402 elements.add(newElement); 403 } 404 void shrinkTo(int newSize) 405 { 406 while(size() > newSize) 407 { 408 elements.remove(newSize); 409 } 410 } 411 List toList() 412 { 413 return(new LinkedList(elements)); 414 } 415 } 416 417 418 /** 419 * Returns a wrapper store which intercedes whenever viewlet data 420 * is get or set. 421 * 422 * Implements method from the MultiViewletDataStore interface 423 * */ 424 public ViewletDataStore getViewletDataStore(ViewletType type) { 425 // Note the outer test is not synchronized to speed up normal code flow 426 // but the test is duplicated within 427 if (wrappedStoreMap == null) { 428 synchronized(this) { 429 if (wrappedStoreMap == null) { 430 wrappedStoreMap=new HashMap(); 431 } 432 } 433 } 434 ViewletDataStore result = (ViewletDataStore)wrappedStoreMap.get(type); 435 if (result == null) { 436 synchronized(this) { 437 if (result == null) { 438 result = 439 new WrappedMultiViewletDataStore(this, type); 440 result.setSymRef(new SymRef(result,this.getSymRef(),type.getClass().getName())); 441 wrappedStoreMap.put(type, result); 442 } 443 } 444 } 445 return result; 446 } 447 448} 449 450 451 452 453 454 455