/*
* Copyright 2013 eXo Platform SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package juzu;
import juzu.impl.common.Tools;
import java.util.HashMap;
import java.util.Iterator;
/**
* A map that holds properties as expressed by {@link PropertyType}. The map can delegate to an optional
* another properties (thus forming a chain) in order to provide a modified version of an existing map
* by wrapping it with a map.
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public class PropertyMap implements Iterable<PropertyType<?>> {
/** . */
private static final Object[] NO_VALUES = new Object[0];
private static class Values implements Iterable<Object> {
/** . */
private Object[] objects = NO_VALUES;
/** . */
private int size = 0;
/** . */
private final PropertyMap owner;
private Values(PropertyMap owner) {
this.owner = owner;
}
private Values(PropertyMap owner, Values that) {
this.owner = owner;
this.objects = that.objects.clone();
this.size = that.size;
}
public Iterator<Object> iterator() {
return Tools.iterator(0, size, objects);
}
void addValue(Object value) {
if (value == null) {
throw new NullPointerException("Value can't be null");
}
if (size >= objects.length) {
Object[] copy = new Object[size + 4];
System.arraycopy(objects, 0, copy, 0, objects.length);
objects = copy;
}
objects[size++] = value;
}
void clear() {
if (size > 0) {
for (int i = size - 1;i >= 0;i--) {
objects[i] = null;
}
size = 0;
}
}
}
/** The current existing values. */
private HashMap<PropertyType<?>, Values> map;
/** An optional delegate. */
private PropertyMap delegate;
public PropertyMap() {
}
public PropertyMap(PropertyMap delegate) {
this.delegate = delegate;
}
private <T> Values get(PropertyType<T> property, boolean recurse, boolean modifiable) {
Values values = null;
//
if (map != null) {
values = map.get(property);
}
//
if (values == null && recurse && delegate != null) {
values = delegate.get(property, true, false);
}
//
if (modifiable) {
if (values == null) {
if (map == null) {
map = new HashMap<PropertyType<?>, Values>();
}
map.put(property, values = new Values(this));
}
else if (values.owner != this) {
if (map == null) {
map = new HashMap<PropertyType<?>, Values>();
}
map.put(property, values = new Values(this, values));
}
}
else if (values != null && values.size == 0) {
values = null;
}
//
return values;
}
public Iterator<PropertyType<?>> iterator() {
return map != null ? map.keySet().iterator() : Tools.<PropertyType<?>>emptyIterator();
}
public <T> T getValue(PropertyType<T> property) {
T value = null;
Values values = get(property, true, false);
if (values != null && values.size > 0) {
value = property.cast(values.objects[0]);
}
return value;
}
public <T> Iterable<T> getValues(PropertyType<T> property) throws NullPointerException {
return (Iterable<T>)get(property, true, false);
}
public <T> void setValue(PropertyType<T> property, T value) throws NullPointerException {
if (value == null) {
remove(property);
}
else {
Values existing = get(property, false, true);
existing.clear();
existing.addValue(value);
}
}
public <T> void setValues(PropertyType<T> property, T... values) throws NullPointerException {
Values existing = get(property, false, true);
existing.clear();
for (T value : values) {
existing.addValue(value);
}
}
public <T> void setValues(PropertyType<T> property, Iterable<? extends T> values) throws NullPointerException {
Values existing = get(property, false, true);
existing.clear();
for (T value : values) {
existing.addValue(value);
}
}
public <T> void addValue(PropertyType<T> property, T value) throws NullPointerException {
get(property, true, true).addValue(value);
}
public <T> void addValues(PropertyType<T> property, T... values) throws NullPointerException {
Values existing = get(property, true, true);
for (T value : values) {
existing.addValue(value);
}
}
public <T> void addValues(PropertyType<T> property, Iterable<? extends T> values) throws NullPointerException {
Values existing = get(property, true, true);
for (T value : values) {
existing.addValue(value);
}
}
public <T> void remove(PropertyType<T> property) throws NullPointerException {
Values values = get(property, false, delegate != null);
if (values != null) {
values.clear();
}
}
public <T> boolean contains(PropertyType<T> property) throws NullPointerException {
Values values = get(property, true, false);
return values != null && values.size > 0;
}
}