/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* 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 org.arakhne.afc.attrs.collection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.attrs.attr.Attribute;
import org.arakhne.afc.attrs.attr.AttributeException;
import org.arakhne.afc.attrs.attr.AttributeImpl;
import org.arakhne.afc.attrs.attr.AttributeType;
import org.arakhne.afc.attrs.attr.AttributeValue;
import org.arakhne.afc.attrs.attr.AttributeValueImpl;
import org.arakhne.afc.references.SoftValueTreeMap;
import org.arakhne.afc.ui.vector.Color;
import org.arakhne.afc.ui.vector.Image;
/**
* This class implements an abstract attribute provider that use
* a memory cache.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("deprecation")
public abstract class BufferedAttributeCollection extends AbstractAttributeCollection {
private static final long serialVersionUID = 1865614675044905721L;
private transient Map<String, AttributeValue> cache = new SoftValueTreeMap<>();
/** Make a deep copy of this object and replies the copy.
*
* @return the deep copy.
*/
@Pure
@Override
public BufferedAttributeCollection clone() {
final BufferedAttributeCollection clone = (BufferedAttributeCollection) super.clone();
this.cache = new SoftValueTreeMap<>();
return clone;
}
/** Load a value from the data source.
*
* @param name is the name of the attribute to load
* @return the value of the attribute.
* @throws AttributeException on error or when the attribute does not exist
*/
protected abstract AttributeValue loadValue(String name) throws AttributeException;
@Pure
@Override
public abstract Collection<String> getAllAttributeNames();
/** Save a value into the data source.
*
* @param name is the name of the attribute to save
* @param value is the value of the attribute.
* @throws AttributeException on error.
*/
protected abstract void saveValue(String name, AttributeValue value) throws AttributeException;
/** Remove a value from the data source.
*
* @param name is the name of the attribute to remove
* @return the removed value
* @throws AttributeException on error
*/
protected abstract AttributeValue removeValue(String name) throws AttributeException;
/** Remove all the values from the data source.
*
* @return <code>true</code> on success, otherwhise <code>false</code>
* @throws AttributeException on error
*/
protected abstract boolean removeAllValues() throws AttributeException;
/** Replies the value associated to the specified name.
*
* <p>This function differs to {@link #extractValueForSafe(String)} because it throws
* an exception if the value cannot be loaded.
*
* @throws AttributeException on error.
* @see #extractValueForSafe(String)
*/
private AttributeValue extractValueFor(String name) throws AttributeException {
final AttributeValue value;
if (this.cache.containsKey(name)) {
value = this.cache.get(name);
} else {
value = loadValue(name);
this.cache.put(name, value);
}
return value;
}
/** Replies the value associated to the specified name.
*
* <p>This function differs to {@link #extractValueFor(String)} because it is not throwing
* an exception if the value cannot be loaded. In this case is is replying <code>null</code>.
*
* @throws AttributeException on error.
* @see #extractValueFor(String)
*/
private AttributeValue extractValueForSafe(String name) {
try {
return extractValueFor(name);
} catch (AttributeException e) {
// Silently ignore the error.
}
return null;
}
@Pure
@Override
public boolean hasAttribute(String name) {
return getAllAttributeNames().contains(name);
}
@Pure
@Override
public Collection<Attribute> getAllAttributes() {
final ArrayList<Attribute> list = new ArrayList<>(getAttributeCount());
for (final String name : getAllAttributeNames()) {
if (name != null) {
try {
final Attribute newAttr = new AttributeImpl(name, extractValueFor(name));
list.add(newAttr);
} catch (AttributeException exception) {
//
}
}
}
return list;
}
@Pure
@Override
public Map<AttributeType, Collection<Attribute>> getAllAttributesByType() {
final Map<AttributeType, Collection<Attribute>> map = new TreeMap<>();
for (final String name : getAllAttributeNames()) {
if (name != null) {
try {
final Attribute newAttr = new AttributeImpl(name, extractValueFor(name));
Collection<Attribute> list = map.get(newAttr.getType());
if (list == null) {
list = new ArrayList<>();
map.put(newAttr.getType(), list);
}
list.add(newAttr);
} catch (AttributeException exception) {
//
}
}
}
return map;
}
@Pure
@Override
public AttributeValue getAttribute(String name) {
final AttributeValue value = extractValueForSafe(name);
if (value != null) {
return new AttributeValueImpl(value);
}
return null;
}
@Pure
@Override
public AttributeValue getAttribute(String name, AttributeValue defaultValue) {
final AttributeValue value = extractValueForSafe(name);
if (value != null) {
return new AttributeValueImpl(value);
}
return defaultValue;
}
@Pure
@Override
public Attribute getAttributeObject(String name) {
final AttributeValue value = extractValueForSafe(name);
if (value != null) {
return new AttributeImpl(name, value);
}
return null;
}
@Override
public void freeMemory() {
this.cache.clear();
}
/** Set the attribute value.
*
* @param name is the name of the attribute
* @param value is the raw value to store.
* @return the new created attribute
* @throws AttributeException on error.
*/
protected Attribute setAttributeFromRawValue(String name, AttributeValue value) throws AttributeException {
return setAttributeFromRawValue(name, value.getType(), value.getValue());
}
/** Set the attribute value.
*
* @param name is the name of the attribute
* @param type is the type of the attribute
* @param value is the raw value to store.
* @return the new created attribute
* @throws AttributeException on error.
*/
protected Attribute setAttributeFromRawValue(String name, AttributeType type, Object value) throws AttributeException {
AttributeValue oldValue = extractValueForSafe(name);
if (oldValue != null) {
if (oldValue.equals(value)) {
return null;
}
// Clone the value for avoid border effects.
oldValue = new AttributeValueImpl(oldValue);
}
final Attribute attr = new AttributeImpl(name, type);
attr.setValue(type.cast(value));
saveValue(name, attr);
this.cache.put(name, attr);
if (oldValue != null) {
fireAttributeChangedEvent(name, oldValue, attr);
} else {
fireAttributeAddedEvent(name, attr);
}
return attr;
}
@Override
public Attribute setAttributeType(String name, AttributeType type) throws AttributeException {
final AttributeValue oldValue = extractValueForSafe(name);
final AttributeType oldType = (oldValue == null) ? null : oldValue.getType();
if (oldValue == null || oldType == null || type == null || type == oldType) {
return null;
}
final Attribute attr = new AttributeImpl(name, oldValue.getValue());
attr.cast(type);
this.cache.put(name, attr);
fireAttributeChangedEvent(name, oldValue, attr);
return attr;
}
@Override
public Attribute setAttribute(String name, AttributeValue value) throws AttributeException {
return setAttributeFromRawValue(name, value);
}
@Override
public Attribute setAttribute(String name, boolean value) {
try {
return setAttributeFromRawValue(name, AttributeType.BOOLEAN, Boolean.valueOf(value));
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, int value) {
try {
return setAttributeFromRawValue(name, AttributeType.INTEGER, Long.valueOf(value));
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, long value) {
try {
return setAttributeFromRawValue(name, AttributeType.INTEGER, Long.valueOf(value));
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, float value) {
try {
return setAttributeFromRawValue(name, AttributeType.REAL, Double.valueOf(value));
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, double value) {
try {
return setAttributeFromRawValue(name, AttributeType.REAL, Double.valueOf(value));
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, String value) {
try {
return setAttributeFromRawValue(name, AttributeType.STRING, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, UUID value) {
try {
return setAttributeFromRawValue(name, AttributeType.UUID, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, URL value) {
try {
return setAttributeFromRawValue(name, AttributeType.URL, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, URI value) {
try {
return setAttributeFromRawValue(name, AttributeType.URI, value);
} catch (AttributeException exception) {
return null;
}
}
/**
* {@inheritDoc}
* @deprecated since 13.0
*/
@Override
@Deprecated
public Attribute setAttribute(String name, Image value) {
try {
return setAttributeFromRawValue(name, AttributeType.IMAGE, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, Date value) {
try {
return setAttributeFromRawValue(name, AttributeType.DATE, value);
} catch (AttributeException exception) {
return null;
}
}
/**
* {@inheritDoc}
* @deprecated since 13.0
*/
@Override
@Deprecated
public Attribute setAttribute(String name, Color value) {
try {
return setAttributeFromRawValue(name, AttributeType.COLOR, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, InetAddress value) {
try {
return setAttributeFromRawValue(name, AttributeType.INET_ADDRESS, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, InetSocketAddress value) {
try {
return setAttributeFromRawValue(name, AttributeType.INET_ADDRESS, value == null ? null : value.getAddress());
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, Enum<?> value) {
try {
return setAttributeFromRawValue(name, AttributeType.ENUMERATION, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(String name, Class<?> value) {
try {
return setAttributeFromRawValue(name, AttributeType.TYPE, value);
} catch (AttributeException exception) {
return null;
}
}
@Override
public Attribute setAttribute(Attribute value) throws AttributeException {
return setAttributeFromRawValue(value.getName(), value);
}
@Override
public boolean removeAttribute(String name) {
try {
if (hasAttribute(name)) {
final AttributeValue currentValue = extractValueFor(name);
this.cache.remove(name);
removeValue(name);
fireAttributeRemovedEvent(name, currentValue);
return true;
}
} catch (AttributeException exception) {
//
}
return false;
}
@Override
public boolean removeAllAttributes() {
try {
if (getAttributeCount() > 0) {
this.cache.clear();
if (removeAllValues()) {
fireAttributeClearedEvent();
return true;
}
}
} catch (AttributeException exception) {
//
}
return false;
}
@Override
public boolean renameAttribute(String oldname, String newname, boolean overwrite) {
try {
final AttributeValue valueForOldName = extractValueForSafe(oldname);
// The source attribute does not exist.
if (valueForOldName == null) {
return false;
}
final AttributeValue oldValueForNewName = extractValueForSafe(newname);
// Target attribute is existing and overwrite was disabled.
if ((!overwrite) && (oldValueForNewName != null)) {
return false;
}
final AttributeValue oldValueCopyForNewName = new AttributeValueImpl(oldValueForNewName);
removeValue(oldname);
this.cache.remove(oldname);
if (valueForOldName instanceof Attribute) {
((Attribute) valueForOldName).setName(newname);
}
saveValue(newname, valueForOldName);
this.cache.put(newname, valueForOldName);
if (oldValueForNewName != null) {
fireAttributeRemovedEvent(newname, oldValueCopyForNewName);
}
fireAttributeRenamedEvent(oldname, newname, valueForOldName);
return true;
} catch (AttributeException exception) {
//
}
return false;
}
@Override
public void flush() {
//
}
}