/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.object;
import com.oracle.truffle.api.object.Property;
import java.util.AbstractSet;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
/**
* Implementation of {@link PropertyMap} as a reverse-order cons (snoc) list.
*/
final class ConsListPropertyMap extends PropertyMap {
private final ConsListPropertyMap car;
private final Property cdr;
private final int size;
private static final ConsListPropertyMap EMPTY = new ConsListPropertyMap();
private ConsListPropertyMap() {
this.car = null;
this.cdr = null;
this.size = 0;
}
private ConsListPropertyMap(ConsListPropertyMap parent, Property added) {
this.car = Objects.requireNonNull(parent);
this.cdr = added;
this.size = parent.size + 1;
}
public static ConsListPropertyMap empty() {
return EMPTY;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size() == 0;
}
public boolean containsKey(Object key) {
for (Map.Entry<Object, Property> entry : reverseOrderEntrySet()) {
if (entry.getKey().equals(key)) {
return true;
}
}
return false;
}
public boolean containsValue(Object value) {
for (Map.Entry<Object, Property> entry : reverseOrderEntrySet()) {
if (entry.getValue().equals(value)) {
return true;
}
}
return false;
}
public Property get(Object key) {
ConsListPropertyMap current = this;
while (!current.isEmpty()) {
Property p = current.getLastProperty();
if (p.getKey().equals(key)) {
return p;
}
current = current.getParentMap();
}
return null;
}
public Set<Object> keySet() {
return new AbstractSet<Object>() {
@Override
public Iterator<Object> iterator() {
return ConsListPropertyMap.this.orderedKeyIterator();
}
@Override
public int size() {
return ConsListPropertyMap.this.size();
}
};
}
public Collection<Property> values() {
return new AbstractSet<Property>() {
@Override
public Iterator<Property> iterator() {
return ConsListPropertyMap.this.orderedValueIterator();
}
@Override
public int size() {
return ConsListPropertyMap.this.size();
}
};
}
public Set<Map.Entry<Object, Property>> entrySet() {
return new AbstractSet<Map.Entry<Object, Property>>() {
@Override
public Iterator<Map.Entry<Object, Property>> iterator() {
@SuppressWarnings("unchecked")
Map.Entry<Object, Property>[] entries = (Map.Entry<Object, Property>[]) new Map.Entry<?, ?>[size()];
Iterator<Map.Entry<Object, Property>> iterator = reverseOrderEntrySet().iterator();
for (int pos = size() - 1; pos >= 0; pos--) {
entries[pos] = iterator.next();
}
return Arrays.asList(entries).iterator();
}
@Override
public int size() {
return ConsListPropertyMap.this.size();
}
};
}
public Set<Map.Entry<Object, Property>> reverseOrderEntrySet() {
return new AbstractSet<Map.Entry<Object, Property>>() {
@Override
public Iterator<Map.Entry<Object, Property>> iterator() {
return new Iterator<Map.Entry<Object, Property>>() {
ConsListPropertyMap current = ConsListPropertyMap.this;
public Entry<Object, Property> next() {
if (hasNext()) {
try {
return new MapEntryImpl(current.cdr);
} finally {
current = current.car;
}
} else {
throw new NoSuchElementException();
}
}
public boolean hasNext() {
return current != empty();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return ConsListPropertyMap.this.size();
}
};
}
@Override
public Iterator<Object> orderedKeyIterator() {
Object[] keys = new Object[size()];
Iterator<Map.Entry<Object, Property>> iterator = reverseOrderEntrySet().iterator();
for (int pos = size() - 1; pos >= 0; pos--) {
keys[pos] = iterator.next().getKey();
}
return Arrays.asList(keys).iterator();
}
@Override
public Iterator<Object> reverseOrderedKeyIterator() {
return reverseOrderKeys().iterator();
}
public Set<Object> reverseOrderKeys() {
return new AbstractSet<Object>() {
@Override
public Iterator<Object> iterator() {
return new Iterator<Object>() {
ConsListPropertyMap current = ConsListPropertyMap.this;
public Object next() {
if (hasNext()) {
try {
return current.cdr.getKey();
} finally {
current = current.car;
}
} else {
throw new NoSuchElementException();
}
}
public boolean hasNext() {
return current != empty();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return ConsListPropertyMap.this.size();
}
};
}
@Override
public Iterator<Property> orderedValueIterator() {
Property[] values = new Property[size()];
Iterator<Map.Entry<Object, Property>> iterator = reverseOrderEntrySet().iterator();
for (int pos = size() - 1; pos >= 0; pos--) {
values[pos] = iterator.next().getValue();
}
return Arrays.asList(values).iterator();
}
@Override
public Iterator<Property> reverseOrderedValueIterator() {
return reverseOrderValues().iterator();
}
public Set<Property> reverseOrderValues() {
return new AbstractSet<Property>() {
@Override
public Iterator<Property> iterator() {
return new Iterator<Property>() {
ConsListPropertyMap current = ConsListPropertyMap.this;
public Property next() {
if (hasNext()) {
try {
return current.cdr;
} finally {
current = current.car;
}
} else {
throw new NoSuchElementException();
}
}
public boolean hasNext() {
return current != empty();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return ConsListPropertyMap.this.size();
}
};
}
private static final class MapEntryImpl implements Map.Entry<Object, Property> {
private final Property backingProperty;
MapEntryImpl(Property backingProperty) {
this.backingProperty = backingProperty;
}
public Object getKey() {
return backingProperty.getKey();
}
public Property getValue() {
return backingProperty;
}
public Property setValue(Property value) {
throw unmodifiableException();
}
}
public PropertyMap copyAndPut(Object key, Property value) {
if (!value.getKey().equals(key)) {
throw new IllegalArgumentException("Key must equal extracted key of property.");
}
return putCopy(value);
}
public ImmutableMap<Object, Property> copyAndRemove(Object key) {
Deque<Property> shelve = new ArrayDeque<>();
ConsListPropertyMap current = this;
while (!current.isEmpty()) {
if (current.getLastProperty().getKey().equals(key)) {
ConsListPropertyMap newMap = current.getParentMap();
for (Property property : shelve) {
newMap = newMap.putCopy(property);
}
return newMap;
} else {
shelve.push(current.getLastProperty());
current = current.getParentMap();
}
}
return this;
}
@Override
public ConsListPropertyMap putCopy(Property value) {
return new ConsListPropertyMap(this, value);
}
@Override
public ConsListPropertyMap removeCopy(Property value) {
Deque<Property> shelve = new ArrayDeque<>();
ConsListPropertyMap current = this;
while (!current.isEmpty()) {
if (current.getLastProperty().equals(value)) {
ConsListPropertyMap newMap = current.getParentMap();
for (Property property : shelve) {
newMap = newMap.putCopy(property);
}
return newMap;
} else {
shelve.push(current.getLastProperty());
current = current.getParentMap();
}
}
return this;
}
@Override
public ConsListPropertyMap replaceCopy(Property oldValue, Property newValue) {
Deque<Property> shelve = new ArrayDeque<>();
ConsListPropertyMap current = this;
while (!current.isEmpty()) {
if (current.getLastProperty().equals(oldValue)) {
ConsListPropertyMap newMap = current.getParentMap();
newMap = newMap.putCopy(newValue);
for (Property property : shelve) {
newMap = newMap.putCopy(property);
}
return newMap;
} else {
shelve.push(current.getLastProperty());
current = current.getParentMap();
}
}
return this;
}
public ConsListPropertyMap getOwningMap(Property value) {
ConsListPropertyMap current = this;
while (!current.isEmpty()) {
if (current.getLastProperty().equals(value)) {
return current;
}
current = current.getParentMap();
}
return null;
}
@Override
public ConsListPropertyMap getParentMap() {
return car;
}
@Override
public Property getLastProperty() {
return cdr;
}
@Override
public String toString() {
return values().toString();
}
}