001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.graph.implementations; 033 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.Collections; 037import java.util.HashMap; 038import java.util.Iterator; 039import java.util.Map; 040 041import org.graphstream.graph.CompoundAttribute; 042import org.graphstream.graph.Element; 043import org.graphstream.graph.NullAttributeException; 044 045/** 046 * A base implementation of an element. 047 * 048 * <p> 049 * This class is the Base class for {@link org.graphstream.graph.Node}, 050 * {@link org.graphstream.graph.Edge} and {@link org.graphstream.graph.Graph}. 051 * An element is made of an unique and arbitrary identifier that identifies it, 052 * and a set of attributes. 053 * </p> 054 * 055 * @since 20040910 056 */ 057public abstract class AbstractElement implements Element { 058 public static enum AttributeChangeEvent { 059 ADD, CHANGE, REMOVE 060 }; 061 062 // Attribute 063 064 // protected static Set<String> emptySet = new HashSet<String>(); 065 066 /** 067 * Tag of this element. 068 */ 069 protected final String id; 070 071 /** 072 * The index of this element. 073 */ 074 private int index; 075 076 /** 077 * Attributes map. This map is created only when needed. It contains pairs 078 * (key,value) where the key is the attribute name and the value an Object. 079 */ 080 protected HashMap<String, Object> attributes = null; 081 082 /** 083 * Vector used when removing attributes to avoid recursive removing. 084 */ 085 protected ArrayList<String> attributesBeingRemoved = null; 086 087 // Construction 088 089 /** 090 * New element. 091 * 092 * @param id 093 * The unique identifier of this element. 094 */ 095 public AbstractElement(String id) { 096 assert id != null : "Graph elements cannot have a null identifier"; 097 this.id = id; 098 } 099 100 // Access 101 102 public String getId() { 103 return id; 104 } 105 106 public int getIndex() { 107 return index; 108 } 109 110 /** 111 * Used by subclasses to change the index of an element 112 * 113 * @param index 114 * the new index 115 */ 116 protected void setIndex(int index) { 117 this.index = index; 118 } 119 120 // XXX UGLY. how to create events in the abstract element ? 121 // XXX The various methods that add and remove attributes will propagate an 122 // event 123 // XXX sometimes this is in response to another event and the 124 // sourceId/timeId is given 125 // XXX sometimes this comes from a direct call to 126 // add/change/removeAttribute() methods 127 // XXX in which case we need to generate a new event (sourceId/timeId) using 128 // the graph 129 // XXX id and a new time. These methods allow access to this. 130 // protected abstract String myGraphId(); // XXX 131 132 // protected abstract long newEvent(); // XXX 133 134 protected abstract boolean nullAttributesAreErrors(); // XXX 135 136 /** 137 * Called for each change in the attribute set. This method must be 138 * implemented by sub-elements in order to send events to the graph 139 * listeners. 140 * 141 * @param sourceId 142 * The source of the change. 143 * @param timeId 144 * The source time of the change, for synchronization. 145 * @param attribute 146 * The attribute name that changed. 147 * @param event 148 * The type of event among ADD, CHANGE and REMOVE. 149 * @param oldValue 150 * The old value of the attribute, null if the attribute was 151 * added. 152 * @param newValue 153 * The new value of the attribute, null if the attribute is about 154 * to be removed. 155 */ 156 protected abstract void attributeChanged(AttributeChangeEvent event, 157 String attribute, Object oldValue, Object newValue); 158 159 /** 160 * @complexity O(log(n)) with n being the number of attributes of this 161 * element. 162 */ 163 // public Object getAttribute( String key ) 164 @SuppressWarnings("all") 165 public <T> T getAttribute(String key) { 166 if (attributes != null) { 167 T value = (T) attributes.get(key); 168 169 if (value != null) 170 return value; 171 } 172 173 if (nullAttributesAreErrors()) 174 throw new NullAttributeException(key); 175 176 return null; 177 } 178 179 /** 180 * @complexity O(log(n*m)) with n being the number of attributes of this 181 * element and m the number of keys given. 182 */ 183 // public Object getFirstAttributeOf( String ... keys ) 184 @SuppressWarnings("all") 185 public <T> T getFirstAttributeOf(String... keys) { 186 Object o = null; 187 188 if (attributes != null) { 189 for (String key : keys) { 190 o = attributes.get(key); 191 192 if (o != null) 193 return (T) o; 194 } 195 } 196 197 if (o == null && nullAttributesAreErrors()) 198 throw new NullAttributeException(); 199 200 return (T) o; 201 } 202 203 /** 204 * @complexity O(log(n)) with n being the number of attributes of this 205 * element. 206 */ 207 // public Object getAttribute( String key, Class<?> clazz ) 208 @SuppressWarnings("all") 209 public <T> T getAttribute(String key, Class<T> clazz) { 210 if (attributes != null) { 211 Object o = attributes.get(key); 212 213 if (o != null && clazz.isInstance(o)) 214 return (T) o; 215 } 216 217 if (nullAttributesAreErrors()) 218 throw new NullAttributeException(key); 219 220 return null; 221 } 222 223 /** 224 * @complexity O(log(n*m)) with n being the number of attributes of this 225 * element and m the number of keys given. 226 */ 227 // public Object getFirstAttributeOf( Class<?> clazz, String ... keys ) 228 @SuppressWarnings("all") 229 public <T> T getFirstAttributeOf(Class<T> clazz, String... keys) { 230 Object o = null; 231 232 if (attributes == null) 233 return null; 234 235 for (String key : keys) { 236 o = attributes.get(key); 237 238 if (o != null && clazz.isInstance(o)) 239 return (T) o; 240 } 241 242 if (nullAttributesAreErrors()) 243 throw new NullAttributeException(); 244 245 return null; 246 } 247 248 /** 249 * @complexity O(log(n)) with n being the number of attributes of this 250 * element. 251 */ 252 public CharSequence getLabel(String key) { 253 if (attributes != null) { 254 Object o = attributes.get(key); 255 256 if (o != null && o instanceof CharSequence) 257 return (CharSequence) o; 258 } 259 260 if (nullAttributesAreErrors()) 261 throw new NullAttributeException(key); 262 263 return null; 264 } 265 266 /** 267 * @complexity O(log(n)) with n being the number of attributes of this 268 * element. 269 */ 270 public double getNumber(String key) { 271 if (attributes != null) { 272 Object o = attributes.get(key); 273 274 if (o != null) { 275 if (o instanceof Number) 276 return ((Number) o).doubleValue(); 277 278 if (o instanceof String) { 279 try { 280 return Double.parseDouble((String) o); 281 } catch (NumberFormatException e) { 282 } 283 } else if (o instanceof CharSequence) { 284 try { 285 return Double 286 .parseDouble(((CharSequence) o).toString()); 287 } catch (NumberFormatException e) { 288 } 289 } 290 } 291 } 292 293 if (nullAttributesAreErrors()) 294 throw new NullAttributeException(key); 295 296 return Double.NaN; 297 } 298 299 /** 300 * @complexity O(log(n)) with n being the number of attributes of this 301 * element. 302 */ 303 @SuppressWarnings("unchecked") 304 public ArrayList<? extends Number> getVector(String key) { 305 if (attributes != null) { 306 Object o = attributes.get(key); 307 308 if (o != null && o instanceof ArrayList) 309 return ((ArrayList<? extends Number>) o); 310 } 311 312 if (nullAttributesAreErrors()) 313 throw new NullAttributeException(key); 314 315 return null; 316 } 317 318 /** 319 * @complexity O(log(n)) with n being the number of attributes of this 320 * element. 321 */ 322 public Object[] getArray(String key) { 323 if (attributes != null) { 324 Object o = attributes.get(key); 325 326 if (o != null && o instanceof Object[]) 327 return ((Object[]) o); 328 } 329 330 if (nullAttributesAreErrors()) 331 throw new NullAttributeException(key); 332 333 return null; 334 } 335 336 /** 337 * @complexity O(log(n)) with n being the number of attributes of this 338 * element. 339 */ 340 public HashMap<?, ?> getHash(String key) { 341 if (attributes != null) { 342 Object o = attributes.get(key); 343 344 if (o != null) { 345 if (o instanceof HashMap<?, ?>) 346 return ((HashMap<?, ?>) o); 347 if (o instanceof CompoundAttribute) 348 return ((CompoundAttribute) o).toHashMap(); 349 } 350 } 351 352 if (nullAttributesAreErrors()) 353 throw new NullAttributeException(key); 354 355 return null; 356 } 357 358 /** 359 * @complexity O(log(n)) with n being the number of attributes of this 360 * element. 361 */ 362 public boolean hasAttribute(String key) { 363 if (attributes != null) 364 return attributes.containsKey(key); 365 366 return false; 367 } 368 369 /** 370 * @complexity O(log(n)) with n being the number of attributes of this 371 * element. 372 */ 373 public boolean hasAttribute(String key, Class<?> clazz) { 374 if (attributes != null) { 375 Object o = attributes.get(key); 376 377 if (o != null) 378 return (clazz.isInstance(o)); 379 } 380 381 return false; 382 } 383 384 /** 385 * @complexity O(log(n)) with n being the number of attributes of this 386 * element. 387 */ 388 public boolean hasLabel(String key) { 389 if (attributes != null) { 390 Object o = attributes.get(key); 391 392 if (o != null) 393 return (o instanceof CharSequence); 394 } 395 396 return false; 397 } 398 399 /** 400 * @complexity O(log(n)) with n being the number of attributes of this 401 * element. 402 */ 403 public boolean hasNumber(String key) { 404 if (attributes != null) { 405 Object o = attributes.get(key); 406 407 if (o != null) 408 return (o instanceof Number); 409 } 410 411 return false; 412 } 413 414 /** 415 * @complexity O(log(n)) with n being the number of attributes of this 416 * element. 417 */ 418 public boolean hasVector(String key) { 419 if (attributes != null) { 420 Object o = attributes.get(key); 421 422 if (o != null && o instanceof ArrayList<?>) 423 return true; 424 } 425 426 return false; 427 } 428 429 /** 430 * @complexity O(log(n)) with n being the number of attributes of this 431 * element. 432 */ 433 public boolean hasArray(String key) { 434 if (attributes != null) { 435 Object o = attributes.get(key); 436 437 if (o != null && o instanceof Object[]) 438 return true; 439 } 440 441 return false; 442 } 443 444 /** 445 * @complexity O(log(n)) with n being the number of attributes of this 446 * element. 447 */ 448 public boolean hasHash(String key) { 449 if (attributes != null) { 450 Object o = attributes.get(key); 451 452 if (o != null 453 && (o instanceof HashMap<?, ?> || o instanceof CompoundAttribute)) 454 return true; 455 } 456 457 return false; 458 } 459 460 public Iterator<String> getAttributeKeyIterator() { 461 if (attributes != null) 462 return attributes.keySet().iterator(); 463 464 return null; 465 } 466 467 public Iterable<String> getEachAttributeKey() { 468 return getAttributeKeySet(); 469 } 470 471 public Collection<String> getAttributeKeySet() { 472 if (attributes != null) 473 return (Collection<String>) Collections 474 .unmodifiableCollection(attributes.keySet()); 475 476 return Collections.emptySet(); 477 } 478 479 // public Map<String,Object> getAttributeMap() 480 // { 481 // if( attributes != null ) 482 // { 483 // if( constMap == null ) 484 // constMap = new ConstMap<String,Object>( attributes ); 485 // 486 // return constMap; 487 // } 488 // 489 // return null; 490 // } 491 492 /** 493 * Override the Object method 494 */ 495 @Override 496 public String toString() { 497 return id; 498 } 499 500 public int getAttributeCount() { 501 if (attributes != null) 502 return attributes.size(); 503 504 return 0; 505 } 506 507 // Command 508 509 public void clearAttributes() { 510 if (attributes != null) { 511 for (Map.Entry<String, Object> entry : attributes.entrySet()) 512 attributeChanged(AttributeChangeEvent.REMOVE, entry.getKey(), 513 entry.getValue(), null); 514 515 attributes.clear(); 516 } 517 } 518 519 protected void clearAttributesWithNoEvent() { 520 if (attributes != null) 521 attributes.clear(); 522 } 523 524 /** 525 * @complexity O(log(n)) with n being the number of attributes of this 526 * element. 527 */ 528 public void addAttribute(String attribute, Object... values) { 529 if (attributes == null) 530 attributes = new HashMap<String, Object>(1); 531 532 Object oldValue; 533 Object value; 534 535 if (values.length == 0) 536 value = true; 537 else if (values.length == 1) 538 value = values[0]; 539 else 540 value = values; 541 542 AttributeChangeEvent event = AttributeChangeEvent.ADD; 543 544 if (attributes.containsKey(attribute)) // In case the value is null, 545 event = AttributeChangeEvent.CHANGE; // but the attribute exists. 546 547 oldValue = attributes.put(attribute, value); 548 attributeChanged(event, attribute, oldValue, value); 549 } 550 551 /** 552 * @complexity O(log(n)) with n being the number of attributes of this 553 * element. 554 */ 555 public void changeAttribute(String attribute, Object... values) { 556 addAttribute(attribute, values); 557 } 558 559 /** 560 * @complexity O(log(n)) with n being the number of attributes of this 561 * element. 562 */ 563 public void setAttribute(String attribute, Object... values) { 564 addAttribute(attribute, values); 565 } 566 567 /** 568 * @complexity O(log(n)) with n being the number of attributes of this 569 * element. 570 */ 571 public void addAttributes(Map<String, Object> attributes) { 572 if (this.attributes == null) 573 this.attributes = new HashMap<String, Object>(attributes.size()); 574 575 Iterator<String> i = attributes.keySet().iterator(); 576 Iterator<Object> j = attributes.values().iterator(); 577 578 while (i.hasNext() && j.hasNext()) 579 addAttribute(i.next(), j.next()); 580 } 581 582 /** 583 * @complexity O(log(n)) with n being the number of attributes of this 584 * element. 585 */ 586 public void removeAttribute(String attribute) { 587 if (attributes != null) { 588 // 589 // 'attributesBeingRemoved' is created only if this is required. 590 // 591 if (attributesBeingRemoved == null) 592 attributesBeingRemoved = new ArrayList<String>(); 593 594 // 595 // Avoid recursive calls when synchronizing graphs. 596 // 597 if (attributes.containsKey(attribute) 598 && !attributesBeingRemoved.contains(attribute)) { 599 attributesBeingRemoved.add(attribute); 600 601 attributeChanged(AttributeChangeEvent.REMOVE, attribute, 602 attributes.get(attribute), null); 603 604 attributesBeingRemoved 605 .remove(attributesBeingRemoved.size() - 1); 606 attributes.remove(attribute); 607 } 608 } 609 } 610}