/* * $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.Map.Entry; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.attrs.attr.Attribute; import org.arakhne.afc.attrs.attr.AttributeError; 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.attrs.collection.AttributeChangeEvent.Type; import org.arakhne.afc.ui.vector.Color; import org.arakhne.afc.ui.vector.Image; /** * This class contains a collection of attribute providers and * tries to gather the data. * This class contains a collection of AttributeProviders and * exhibites the values of the attributes of all these providers. * This class follows the following rules (in that order) * to retreive the value of an attribute:<ol> * <li>If the attribute is defined in none of the containers, throws the standard exception;</li> * <li>If the attribute is defined in only one of the containers, replies the attribute value itself;</li> * <li>If the attribute is defined in more than one container:<ol> * <li>if all the values are equal, then replies one of the attribute values;</li> * <li>if the values are not equal and all the values have equivalent types (as replied * by {@link AttributeType#isAssignableFrom(AttributeType)}), then replies an * attribute value with a "undefined" value and of the type of one of the values;</li> * <li>if the values are not equal and one of the value has not an equivalent type to * the others (as replied by {@link AttributeType#isAssignableFrom(AttributeType)}), * then replies an attribute value with a "undefined" value and of the type OBJECT.</li> * </ol></li> * </ol> * * <p>If an attribute is set from this AttributeProviderContainer, all the containers inside it * are changed. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 4.0 */ @SuppressWarnings("deprecation") public class MultiAttributeCollection extends MultiAttributeProvider implements AttributeCollection { private static final long serialVersionUID = 6542692326662357040L; /** * Boolean indicates that enable event handling from the providers, or not. */ AtomicBoolean runProviderEvents = new AtomicBoolean(true); private Handler eventHandler = new Handler(); private Collection<AttributeChangeListener> listeners; private boolean isEventFirable = true; @Pure @Override public synchronized boolean isEventFirable() { return this.isEventFirable; } @Override public synchronized void setEventFirable(boolean isFirable) { this.isEventFirable = isFirable; } @Override public MultiAttributeCollection clone() { final MultiAttributeCollection clone = (MultiAttributeCollection) super.clone(); clone.runProviderEvents = new AtomicBoolean(true); clone.eventHandler = new Handler(); clone.listeners = (this.listeners == null) ? null : new ArrayList<>(this.listeners); for (final AttributeProvider c : clone.containers()) { if (c instanceof AttributeCollection) { ((AttributeCollection) c).addAttributeChangeListener(clone.eventHandler); } } return clone; } @Override public boolean addAttributeContainer(AttributeProvider container) { if (super.addAttributeContainer(container)) { if (container instanceof AttributeCollection) { ((AttributeCollection) container).addAttributeChangeListener(this.eventHandler); } return true; } return false; } @Override public boolean removeAttributeContainer(AttributeProvider container) { if (super.removeAttributeContainer(container)) { if (container instanceof AttributeCollection) { ((AttributeCollection) container).removeAttributeChangeListener(this.eventHandler); } return true; } return false; } @Override public void addAttributeChangeListener(AttributeChangeListener listener) { if (this.listeners == null) { this.listeners = new ArrayList<>(); } this.listeners.add(listener); } @Override public void removeAttributeChangeListener(AttributeChangeListener listener) { if (this.listeners != null) { this.listeners.add(listener); if (this.listeners.isEmpty()) { this.listeners = null; } } } /** Notifies the listeners about the change of an attribute. * * @param event the event. */ protected void fireAttributeChange(AttributeChangeEvent event) { if (this.listeners != null && isEventFirable()) { final AttributeChangeListener[] list = new AttributeChangeListener[this.listeners.size()]; this.listeners.toArray(list); for (final AttributeChangeListener listener : list) { listener.onAttributeChangeEvent(event); } } } @Override public void flush() { for (final AttributeProvider c : containers()) { if (c instanceof AttributeCollection) { ((AttributeCollection) c).flush(); } } } @Override public boolean removeAllAttributes() { final boolean b = this.runProviderEvents.getAndSet(false); try { boolean changed = false; for (final AttributeProvider c : containers()) { if (c instanceof AttributeCollection) { changed = ((AttributeCollection) c).removeAllAttributes() || changed; } } if (changed) { this.cache.clear(); this.names = null; fireAttributeChange(new AttributeChangeEvent(this, Type.REMOVE_ALL, null, null, null, null)); } return changed; } finally { this.runProviderEvents.set(b); } } @Override public boolean removeAttribute(String name) { final boolean b = this.runProviderEvents.getAndSet(false); try { boolean changed = false; final ManyValueAttributeValue oldValue = new ManyValueAttributeValue(); for (final AttributeProvider c : containers()) { assign(oldValue, c.getAttribute(name)); if (c instanceof AttributeCollection) { changed = ((AttributeCollection) c).removeAttribute(name) || changed; } } if (changed) { final AttributeValue value = canonize(oldValue); this.cache.remove(name); if (this.names != null) { this.names.remove(name); if (this.names.isEmpty()) { this.names = null; } } fireAttributeChange(new AttributeChangeEvent(this, Type.REMOVAL, name, value, null, null)); } return changed; } finally { this.runProviderEvents.set(b); } } @Override public boolean renameAttribute(String oldname, String newname) { final boolean b = this.runProviderEvents.getAndSet(false); try { boolean changed = false; final ManyValueAttributeValue currentValue = new ManyValueAttributeValue(); for (final AttributeProvider c : containers()) { assign(currentValue, c.getAttribute(oldname)); if (c instanceof AttributeCollection) { changed = ((AttributeCollection) c).renameAttribute(oldname, newname) || changed; } } if (changed) { final AttributeValue cValue = canonize(currentValue); this.cache.remove(oldname); this.cache.remove(newname); if (this.names != null) { this.names.remove(oldname); this.names.add(newname); } fireAttributeChange(new AttributeChangeEvent(this, Type.RENAME, oldname, cValue, newname, cValue)); } return changed; } finally { this.runProviderEvents.set(b); } } @Override public boolean renameAttribute(String oldname, String newname, boolean overwrite) { final boolean b = this.runProviderEvents.getAndSet(false); try { boolean changed = false; final ManyValueAttributeValue currentValue = new ManyValueAttributeValue(); for (final AttributeProvider c : containers()) { assign(currentValue, c.getAttribute(oldname)); if (c instanceof AttributeCollection) { changed = ((AttributeCollection) c).renameAttribute(oldname, newname, overwrite) || changed; } } if (changed) { final AttributeValue cValue = canonize(currentValue); this.cache.remove(oldname); if (this.names != null) { this.names.remove(oldname); this.names.add(newname); } fireAttributeChange(new AttributeChangeEvent(this, Type.RENAME, oldname, cValue, newname, cValue)); } return changed; } finally { this.runProviderEvents.set(b); } } @Override public void addAttributes(AttributeProvider content) throws AttributeException { for (final Attribute attr : content.attributes()) { setAttribute(attr); } } @Override public void addAttributes(Map<String, Object> content) { AttributeType type; Object rawValue; for (final Entry<String, Object> entry : content.entrySet()) { rawValue = entry.getValue(); type = AttributeType.fromValue(rawValue); rawValue = type.cast(rawValue); try { setAttribute(entry.getKey(), new AttributeValueImpl(type, rawValue)); } catch (AttributeException exception) { // should never occur } } } @Override public void setAttributes(AttributeProvider content) throws AttributeException { removeAllAttributes(); for (final Attribute attr : content.attributes()) { setAttribute(attr); } } @Override public void setAttributes(Map<String, Object> content) { removeAllAttributes(); AttributeType type; Object value; for (final Entry<String, Object> entry : content.entrySet()) { value = entry.getValue(); type = AttributeType.fromValue(value); value = type.cast(value); try { setAttribute(entry.getKey(), new AttributeValueImpl(type, value)); } catch (AttributeException e) { throw new AttributeError(e); } } } @Override public Attribute setAttribute(String name, AttributeValue value) throws AttributeException { final boolean b = this.runProviderEvents.getAndSet(false); try { boolean changed = false; Attribute attr; final ManyValueAttributeValue oldValue = new ManyValueAttributeValue(); final ManyValueAttributeValue newValue = new ManyValueAttributeValue(); for (final AttributeProvider c : containers()) { assign(oldValue, c.getAttribute(name)); if (c instanceof AttributeCollection) { attr = ((AttributeCollection) c).setAttribute(name, value); assign(newValue, attr); if (attr != null) { changed = true; } } else { assign(newValue, null); } } if (changed) { final AttributeValue oValue = canonize(oldValue); final AttributeValue nValue = canonize(newValue); this.cache.put(name, nValue); final AttributeChangeEvent event; if (nValue == null) { event = new AttributeChangeEvent(this, Type.REMOVAL, name, oValue, null, null); } else if (oValue != null && oValue.isAssigned()) { event = new AttributeChangeEvent(this, Type.VALUE_UPDATE, name, oValue, name, nValue); } else { event = new AttributeChangeEvent(this, Type.ADDITION, null, null, name, nValue); } fireAttributeChange(event); return new AttributeImpl(name, value); } return null; } finally { this.runProviderEvents.set(b); } } @Override public Attribute setAttribute(String name, boolean value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, int value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, long value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, float value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, double value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, String value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, InetAddress value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, InetSocketAddress value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, Enum<?> value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, Class<?> value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, UUID value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, URL value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, URI value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } /** {@inheritDoc} * * @deprecated since 13.0 */ @Override @Deprecated public Attribute setAttribute(String name, Image value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(String name, Date value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } /** {@inheritDoc} * * @deprecated since 13.0 */ @Override @Deprecated public Attribute setAttribute(String name, Color value) { try { return setAttribute(name, new AttributeValueImpl(value)); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttribute(Attribute value) throws AttributeException { if (value == null) { return null; } try { return setAttribute(value.getName(), value); } catch (AttributeException exception) { return null; } } @Override public Attribute setAttributeType(String name, AttributeType type) throws AttributeException { final boolean b = this.runProviderEvents.getAndSet(false); try { final ManyValueAttributeValue oldValue = new ManyValueAttributeValue(); final ManyValueAttributeValue attr = new ManyValueAttributeValue(); Attribute a; boolean changed = false; for (final AttributeProvider c : containers()) { assign(oldValue, c.getAttribute(name)); if (c instanceof AttributeCollection) { a = ((AttributeCollection) c).setAttributeType(name, type); assign(attr, a); if (a != null) { changed = true; } } } final AttributeValue cValue = canonize(attr); if (changed) { final AttributeValue oValue = canonize(oldValue); this.cache.put(name, cValue); fireAttributeChange(new AttributeChangeEvent(this, Type.VALUE_UPDATE, name, oValue, name, cValue)); } return new AttributeImpl(name, cValue); } finally { this.runProviderEvents.set(b); } } /** * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 4.0 */ private class Handler implements AttributeChangeListener { Handler() { // } @Override public void onAttributeChangeEvent(AttributeChangeEvent event) { if (MultiAttributeCollection.this.runProviderEvents.get()) { switch (event.getType()) { case ADDITION: MultiAttributeCollection.this.cache.remove(event.getName()); if (MultiAttributeCollection.this.names != null) { MultiAttributeCollection.this.names.add(event.getName()); } break; case REMOVAL: MultiAttributeCollection.this.cache.remove(event.getOldName()); if (MultiAttributeCollection.this.names != null) { MultiAttributeCollection.this.names.remove(event.getOldName()); } break; case REMOVE_ALL: freeMemory(); break; case RENAME: caseRenameAttributeChange(event); break; case VALUE_UPDATE: MultiAttributeCollection.this.cache.remove(event.getName()); break; default: } } } } private void caseRenameAttributeChange(AttributeChangeEvent event) { MultiAttributeCollection.this.cache.remove(event.getOldName()); MultiAttributeCollection.this.cache.remove(event.getName()); if (MultiAttributeCollection.this.names != null) { MultiAttributeCollection.this.names.remove(event.getOldName()); MultiAttributeCollection.this.names.remove(event.getName()); } } }