/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation.
*/
package org.geogebra.common.kernel.algos;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Set to store AlgoElement objects for updating.
*/
public class AlgorithmSet {
private HashMap<AlgoElement, AlgoElement> hashMap;
private Link head, tail;
private int size;
/**
* Creates new algorithm set
*/
public AlgorithmSet() {
size = 0;
}
/**
* Returns number of algos
*
* @return number of algos
*/
final public int getSize() {
return size;
}
/**
* Returns true iff empty
*
* @return true iff empty
*/
final public boolean isEmpty() {
return size == 0;
}
/**
* Inserts algo into set sorted by constructionIndex. Note: this leads to a
* topological sorting of the algorithms which is important for updating.
*
* @return true = the algo was added, false = the algo was already in the
* set
* @param algo
* algo to be added
*/
final public boolean add(AlgoElement algo) {
if (contains(algo)) {
return false;
}
// empty list?
if (getHead() == null) {
if (hashMap == null) {
hashMap = new HashMap<AlgoElement, AlgoElement>();
}
hashMap.put(algo, algo);
setHead(new Link(algo, null));
tail = getHead();
size++;
return true;
}
// check if algo is already at end of list
if (tail.algo == algo) {
return false;
}
/*
* Usually we can just add an algorithm at the end of the list to have
* it at the right place for updating. However, in certain cases an
* algorithm needs to be inserted at an earlier place right after a
* certain parentAlgo. For example, in a regular polygon, new segments
* can be created later that need to be inserted directly after the
* parent polygon.
*/
// check if algo needs to be inserted right after a certain parentAlgo
AlgoElement parentAlgo = algo.getUpdateAfterAlgo();
// Standard case: insert at end of list
if (parentAlgo == null || parentAlgo == tail.algo
|| !contains(parentAlgo)) {
tail.next = new Link(algo, null);
tail = tail.next;
}
// Special case: insert in the middle, right after parentAlgo
else {
// search for parentAlgo
Link cur = getHead();
while (cur.algo != parentAlgo) {
cur = cur.next;
}
// now cur.algo == parentAlgo, insert right afterwards
cur.next = new Link(algo, cur.next);
}
hashMap.put(algo, algo);
size++;
return true;
}
final private boolean addSorted(AlgoElement algo) {
if (contains(algo)) {
return false;
}
// empty list?
if (getHead() == null) {
if (hashMap == null) {
hashMap = new HashMap<AlgoElement, AlgoElement>();
}
hashMap.put(algo, algo);
setHead(new Link(algo, null));
tail = getHead();
size++;
return true;
}
// check if algo is already at end of list
if (tail.algo == algo) {
return false;
}
/*
* Usually we can just add an algorithm at the end of the list to have
* it at the right place for updating. However, in certain cases an
* algorithm needs to be inserted at an earlier place right after a
* certain parentAlgo. For example, in a regular polygon, new segments
* can be created later that need to be inserted directly after the
* parent polygon.
*/
// check if algo needs to be inserted right after a certain parentAlgo
AlgoElement parentAlgo = algo.getUpdateAfterAlgo();
// Standard case: insert at end of list
if (parentAlgo == tail.algo) {
tail.next = new Link(algo, null);
tail = tail.next;
} else if (parentAlgo == null || !contains(parentAlgo)) {
Link cur = getHead();
while (cur.algo.getID() < algo.getID() && cur.next != null) {
cur = cur.next;
}
cur.next = new Link(algo, cur.next);
}
// Special case: insert in the middle, right after parentAlgo
else {
// search for parentAlgo
Link cur = getHead();
while (cur.algo != parentAlgo) {
cur = cur.next;
}
// now cur.algo == parentAlgo, insert right afterwards
cur.next = new Link(algo, cur.next);
}
hashMap.put(algo, algo);
size++;
return true;
}
/**
* Inserts all algos of set at the end of this set.
*
* @param algoSet
* set of algos to be added
*/
public void addAll(AlgorithmSet algoSet) {
Link cur = algoSet.getHead();
while (cur != null) {
add(cur.algo);
cur = cur.next;
}
}
/**
* Inserts all algos of set into this set.
*
* @param algoSet
* set of algos to be added
*/
public void addAllSorted(AlgorithmSet algoSet) {
Link cur = algoSet.getHead();
while (cur != null) {
addSorted(cur.algo);
cur = cur.next;
}
}
/**
* Returns true if this set contains algo.
*
* @return true iff this set contains algo.
* @param algo
* algorithm
*/
final public boolean contains(AlgoElement algo) {
if (size == 0 || algo == null) {
return false;
}
return hashMap.get(algo) != null;
}
/**
* Removes algo from set.
*
* @return true if found and removed, false if not found
* @param algo
* algo to be removed
*/
final public boolean remove(AlgoElement algo) {
Object remObj = hashMap.remove(algo);
if (remObj == null) {
return false;
}
Link prev = null;
Link cur = getHead();
while (cur != null) {
// found algo to remove
if (cur.algo == algo) {
if (prev == null) { // remove from head
setHead(cur.next);
if (getHead() == null) {
tail = null;
}
} else { // standard case
prev.next = cur.next;
if (prev.next == null) {
tail = prev;
}
}
size--;
return true;
}
// not yet found
prev = cur;
cur = cur.next;
}
return false;
}
/**
* Updates all algorithms of this set.
*/
final public void updateAll() {
Link cur = getHead();
while (cur != null) {
cur.algo.update();
cur = cur.next;
}
}
/**
* Updates all algorithms of this set until the given algorithm is reached.
*
* @param lastAlgoToUpdate
* last algorithm to update
*/
final public void updateAllUntil(AlgoElement lastAlgoToUpdate) {
Link cur = getHead();
while (cur != null) {
cur.algo.update();
if (cur.algo == lastAlgoToUpdate) {
return;
}
cur = cur.next;
}
}
/**
* Adds all algorithms in this set to the given collection
*
* @param collection
* collection of algorithms
*/
final public void addAllToCollection(Collection<AlgoElement> collection) {
Link cur = getHead();
while (cur != null) {
collection.add(cur.algo);
cur = cur.next;
}
}
/**
* Removes all algorithms in this set from the given collection
*
* @param collection
* collection of algorithms
*/
final public void removeAllFromCollection(
Collection<AlgoElement> collection) {
Link cur = getHead();
while (cur != null) {
collection.remove(cur.algo);
cur = cur.next;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AlgorithmSet[");
Link cur = getHead();
while (cur != null) {
sb.append("\n\t");
sb.append(cur.algo + ", constIndex: "
+ cur.algo.getConstructionIndex() + ", ceID: "
+ cur.algo.getID());
cur = cur.next;
}
sb.append("]");
return sb.toString();
}
private static class Link {
AlgoElement algo;
Link next;
Link(AlgoElement a, Link n) {
algo = a;
next = n;
}
}
/**
* Returns iterator for this set
*
* @return iterator for this set
*/
public AlgorithmSetIterator getIterator() {
return new AlgorithmSetIterator();
}
/**
* @return the head
*/
public Link getHead() {
return head;
}
/**
* @param head
* the head to set
*/
public void setHead(Link head) {
this.head = head;
}
/**
* Iterator for this set
*/
public class AlgorithmSetIterator implements Iterator<AlgoElement> {
private Link cur = getHead();
@Override
public void remove() {
AlgorithmSet.this.remove(cur.algo);
cur = cur.next;
}
@Override
public boolean hasNext() {
return cur != null;
}
@Override
public AlgoElement next() {
if (cur == null) {
throw new NoSuchElementException();
}
AlgoElement ret = cur.algo;
cur = cur.next;
return ret;
}
}
}