/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution 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, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.data;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.List;
public class AttributeSetImpl extends AbstractAttributeSet {
private class AttrIterator implements Iterator<Attribute<?>> {
Node n;
AttrIterator(Node n) {
this.n = n;
}
public boolean hasNext() {
return n != null;
}
public Attribute<?> next() {
Node ret = n;
n = n.next;
return ret.attr;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private class AttrList extends AbstractList<Attribute<?>> {
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public Attribute<?> get(int i) {
Node n = head;
int remaining = i;
while (remaining != 0 && n != null) {
n = n.next;
--remaining;
}
if (remaining != 0 || n == null) {
throw new IndexOutOfBoundsException(i + " not in list " + " ["
+ count + " elements]");
}
return n.attr;
}
@Override
public int indexOf(Object o) {
Node n = head;
int ret = 0;
while (n != null) {
if (o.equals(n.attr))
return ret;
n = n.next;
++ret;
}
return -1;
}
@Override
public Iterator<Attribute<?>> iterator() {
return new AttrIterator(head);
}
@Override
public int size() {
return count;
}
}
private static class Node {
Attribute<?> attr;
Object value;
boolean is_read_only;
Node next;
Node(Attribute<?> attr, Object value, boolean is_read_only, Node next) {
this.attr = attr;
this.value = value;
this.is_read_only = is_read_only;
this.next = next;
}
Node(Node other) {
this.attr = other.attr;
this.value = other.value;
this.is_read_only = other.is_read_only;
this.next = other.next;
}
}
private AttrList list = new AttrList();
private Node head = null;
private Node tail = null;
private int count = 0;
public AttributeSetImpl() {
}
public AttributeSetImpl(Attribute<Object>[] attrs, Object[] values) {
if (attrs.length != values.length) {
throw new IllegalArgumentException("arrays must have same length");
}
for (int i = 0; i < attrs.length; i++) {
addAttribute(attrs[i], values[i]);
}
}
public <V> void addAttribute(Attribute<? super V> attr, V value) {
if (attr == null) {
throw new IllegalArgumentException("Adding null attribute");
}
if (findNode(attr) != null) {
throw new IllegalArgumentException("Attribute " + attr
+ " already created");
}
Node n = new Node(attr, value, false, null);
if (head == null)
head = n;
else
tail.next = n;
tail = n;
++count;
fireAttributeListChanged();
}
@Override
protected void copyInto(AbstractAttributeSet destObj) {
AttributeSetImpl dest = (AttributeSetImpl) destObj;
if (this.head != null) {
dest.head = new Node(head);
Node copy_prev = dest.head;
Node cur = this.head.next;
while (cur != null) {
Node copy_cur = new Node(cur);
copy_prev.next = copy_cur;
copy_prev = copy_cur;
cur = cur.next;
}
dest.tail = copy_prev;
dest.count = this.count;
}
}
//
// private helper methods
//
private Node findNode(Attribute<?> attr) {
for (Node n = head; n != null; n = n.next) {
if (n.attr.equals(attr))
return n;
}
return null;
}
//
// attribute access methods
//
@Override
public List<Attribute<?>> getAttributes() {
return list;
}
//
// value access methods
//
@Override
public <V> V getValue(Attribute<V> attr) {
Node n = findNode(attr);
if (n == null) {
throw new IllegalArgumentException("Unknown attribute " + attr);
}
@SuppressWarnings("unchecked")
V ret = (V) n.value;
return ret;
}
//
// read-only methods
//
@Override
public boolean isReadOnly(Attribute<?> attr) {
Node n = findNode(attr);
if (n == null) {
throw new IllegalArgumentException("Unknown attribute " + attr);
}
return n.is_read_only;
}
public void removeAttribute(Attribute<?> attr) {
Node prev = null;
Node n = head;
while (n != null) {
if (n.attr.equals(attr)) {
if (tail == n)
tail = prev;
if (prev == null)
head = n.next;
else
prev.next = n.next;
--count;
fireAttributeListChanged();
return;
}
prev = n;
n = n.next;
}
throw new IllegalArgumentException("Attribute " + attr + " absent");
}
@Override
public void setReadOnly(Attribute<?> attr, boolean value) {
Node n = findNode(attr);
if (n == null) {
throw new IllegalArgumentException("Unknown attribute " + attr);
}
n.is_read_only = value;
}
@Override
public <V> void setValue(Attribute<V> attr, V value) {
if (value instanceof String) {
value = attr.parse((String) value);
}
Node n = findNode(attr);
if (n == null) {
throw new IllegalArgumentException("Unknown attribute " + attr);
}
if (n.is_read_only) {
throw new IllegalArgumentException("Attribute " + attr
+ " is read-only");
}
if (value.equals(n.value)) {
; // do nothing - why change what's already there?
} else {
@SuppressWarnings("unchecked")
V oldvalue = (V) n.value;
n.value = value;
fireAttributeValueChanged(attr, value, oldvalue);
}
}
}