
eu.mihosoft.vrl.visual.IDArrayList Maven / Gradle / Ivy
/*
* IDArrayList.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007–2018 by Michael Hoffer,
* Copyright (c) 2015–2018 G-CSC, Uni Frankfurt,
* Copyright (c) 2009–2015 Steinbeis Forschungszentrum (STZ Ölbronn)
*
* This file is part of Visual Reflection Library (VRL).
*
* VRL is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* see: http://opensource.org/licenses/LGPL-3.0
* file://path/to/VRL/src/eu/mihosoft/vrl/resources/license/lgplv3.txt
*
* VRL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* This version of VRL includes copyright notice and attribution requirements.
* According to the LGPL this information must be displayed even if you modify
* the source code of VRL. Neither the VRL Canvas attribution icon nor any
* copyright statement/attribution may be removed.
*
* Attribution Requirements:
*
* If you create derived work you must do three things regarding copyright
* notice and author attribution.
*
* First, the following text must be displayed on the Canvas:
* "based on VRL source code". In this case the VRL canvas icon must be removed.
*
* Second, the copyright notice must remain. It must be reproduced in any
* program that uses VRL.
*
* Third, add an additional notice, stating that you modified VRL. A suitable
* notice might read
* "VRL source code modified by YourName 2012".
*
* Note, that these requirements are in full accordance with the LGPL v3
* (see 7. Additional Terms, b).
*
* Please cite the publication(s) listed below.
*
* Publications:
*
* M. Hoffer, C. Poliwoda, & G. Wittum. (2013). Visual reflection library:
* a framework for declarative GUI programming on the Java platform.
* Computing and Visualization in Science, 2013, 16(4),
* 181–192. http://doi.org/10.1007/s00791-014-0230-y
*/
package eu.mihosoft.vrl.visual;
import eu.mihosoft.vrl.system.Constants;
import eu.mihosoft.vrl.system.VMessage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* This is an extended version of ArrayList
. Its purpose is to give
* each element in the list a unique ID value which is independant of the
* position in the list. For a big number of elements HashMap is probably more
* efficient in many cases.
*
* @author Michael Hoffer
* @param
*/
public class IDArrayList extends ArrayList {
private static final long serialVersionUID = 4312393627675210967L;
/**
* @return the DEBUG
*/
public static boolean isDebug() {
return DEBUG;
}
/**
* @param state the DEBUG state to set
*/
public static void setDebug(boolean state) {
DEBUG = state;
}
private IDTable idTable = new IDTable();
private final IDTable originalIdTable = new IDTable();
private static boolean DEBUG;
private final List threads = new ArrayList();
private final Object lock = new Object();
@Override
public boolean add(E e) {
synchronized (lock) {
int id = getNewId();
e.setID(id);
if (getById(id) != null) {
System.err.println(
"ID already exists : "
+ getById(id).getID() + " : " + getById(id));
VMessage.criticalErrorDetected();
}
// System.out.println(">> IDArrayList: entry added.");
boolean result = super.add(e);
updateToolTips();
updateIdTable();
return result;
}
}
/**
* Method for debugging multithreading. NOTE: This method must not be
* accessible via the public API.
*/
private void checkThatAccessIsSingleThreaded() {
if (!threads.contains(Thread.currentThread())) {
System.out.println(">> IDArrayList: adding thread "
+ Thread.currentThread());
threads.add(Thread.currentThread());
}
if (threads.size() > 1) {
System.err.println(">> IDArrayList: accessing this list from more"
+ " than one thread is not supported! Number of threads = " + threads.size());
VMessage.criticalErrorDetected();
}
}
/**
* Just used for debugging
*/
public void updateToolTips() {
synchronized (lock) {
if (isDebug()) {
int i = 0;
for (IDObject iDObject : this) {
if (iDObject instanceof Connector) {
Connector c = (Connector) iDObject;
c.getTransferable().setToolTipText(
"Index: " + i + ", Id: " + c.getID());
}
i++;
}
}
}
}
@Override
public void add(int index, E e) {
checkThatAccessIsSingleThreaded();
synchronized (lock) {
int id = getNewId();
e.setID(id);
super.add(index, e);
updateToolTips();
updateIdTable();
}
}
/**
* Adds an element with specific id.
*
* @param e the element to add
* @param ID the id to set
* @return true
(as specified by {@link Collection#add})
*/
public boolean addWithID(E e, int ID) {
synchronized (lock) {
boolean result = false;
if (getById(ID) != null) {
System.out.println(">> IDArrayList: element with equal id already"
+ " exists!");
VMessage.criticalErrorDetected();
} else {
e.setID(ID);
result = super.add(e);
}
updateToolTips();
updateIdTable();
return result;
}
}
/**
* Returns an element by id.
*
* @param ID the id of the element that is to be returned
* @return the element with specified id if such an element exists;
* null
otherwise
*/
public E getById(int ID) {
synchronized (lock) {
E element = null;
for (E e : this) {
if (e.getID() == ID) {
element = e;
}
}
return element;
}
}
/**
* Returns an element index (the position in the array) by id.
*
* @param ID the id of the element
* @return the element index with specified id if such an element exists;
* null
otherwise
*/
public Integer getIndexById(int ID) {
synchronized (lock) {
Integer result = null;
for (int i = 0; i < size(); i++) {
if (get(i).getID() == ID) {
result = i;
}
}
return result;
}
}
/**
* Returns an element id (the position in the array) by index.
*
* @param index the index of the element
* @return the element id with specified index if such an element exists;
* null
otherwise
*/
public Integer getIdByIndex(int index) {
synchronized (lock) {
return get(index).getID();
}
}
/**
* Returns the shortest available (unused) id value (id > 0). This method
* prevents fragmentation and ensures that all values < maxId are really
* used.
* If the id table contains an entry at the current index (
* index=this.size()
) this entry will be used as id.
*
* @return the shortest available id value (id > 0) or an id value defined
* by the id table
* @see IDTable
*/
public int getNewId() {
synchronized (lock) {
// System.out.println(">> *** ID-Search ***");
int result = 0;
if (originalIdTable.size() > size()) {
result = originalIdTable.get(size());
// System.out.println("ID FROM TABLE: " + result);
} else {
int maxId = getMaxId();
boolean foundUnusedId = true;
// we want to find the smallest unused id value
for (int i = 0; i <= maxId; i++) {
foundUnusedId = true;
for (IDObject o : this) {
// System.out.println(">> i = " + i + ", id = " + o.getID());
if (o.getID() == i) {
foundUnusedId = false;
}
}
if (foundUnusedId) {
result = i;
// System.out.println(">> found id = " + id);
break;
}
}
// if we didn't find an unused id smaller than maxId then
// we use id=maxId+1, which isn't in use
if (!foundUnusedId) {
result = maxId + 1;
}
// System.out.println("NEW ID: " + result);
}
// System.out.println(">> id = " + id);
return result;
}
}
/**
* Returns the maximum id value that has been assigned to an IDObject entry.
*
* Warning: it returns 0, even 0 is not in use.
*
* @return the maximum id value, valid range: [0,MAX_INT]
*/
private int getMaxId() {
int max = 0;
for (E e : this) {
max = Math.max(e.getID(), max);
}
// System.out.println(">> max = " + max);
return max;
}
private void updateIdTable() {
idTable.clear();
for (IDObject idObj : this) {
idTable.add(idObj.getID());
}
}
/**
* Defines an id table for this list.
*
* @param idTable the idTable to set
*/
public void setIdTable(IDTable idTable) {
synchronized (lock) {
this.idTable = idTable;
// now set the id of every object entry to the value defined in the
// id table
int size = Math.min(size(), idTable.size());
for (int i = 0; i < size(); i++) {
if (i < size) {
get(i).setID(idTable.get(i));
} else if (i < size()) {
get(i).setID(getNewId());
}
}
for (Integer i : idTable) {
originalIdTable.add(i);
}
updateToolTips();
updateIdTable();
}
}
/**
* Returns the id table of this list.
*
* @return the id table of this list
*/
public IDTable getIdTable() {
synchronized (lock) {
return idTable;
}
}
@Override
public E remove(int index) {
synchronized (lock) {
E result = super.remove(index);
updateToolTips();
updateIdTable();
return result;
}
}
/**
* Removes an entry defined by its id.
*
* @param id the id of the entry to remove
* @return true
(as specified by {@link Collection#remove})
*/
public boolean removeByID(int id) {
return remove(getById(id));
}
@Override
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
synchronized (lock) {
boolean result = false;
if (o instanceof IDObject) {
result = super.remove((E) o);
}
updateToolTips();
updateIdTable();
return result;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy