| /******************************************************************************* |
| * Copyright (c) 2013 CEA LIST. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Cedric Dumoulin - cedric.dumoulin@lifl.fr |
| ******************************************************************************/ |
| package org.eclipse.papyrus.internal.infra.gmfdiag.layers.model.layers.util; |
| |
| import static org.eclipse.papyrus.internal.infra.gmfdiag.layers.model.Activator.log; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.common.util.EMap; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.util.EObjectResolvingEList; |
| import org.eclipse.papyrus.internal.infra.gmfdiag.layers.model.BadStateException; |
| import org.eclipse.papyrus.internal.infra.gmfdiag.layers.model.LayersException; |
| import org.eclipse.papyrus.internal.infra.gmfdiag.layers.model.NotFoundException; |
| import org.eclipse.papyrus.internal.infra.gmfdiag.layers.model.layers.Property; |
| |
| |
| /** |
| * An EMF {@link EObjectResolvingEList} that automaticaly reflect a |
| * Map of (String, T). The keys are the names of the Property. |
| * The list also know the list of Properties, and use it to indexing |
| * the types of the Map. <br> |
| * usage: <br> |
| * list = new PropertyIndexedList<type>(...); |
| * list.setPropertyList( propertyRegistry.getProperties() ); |
| * |
| * @author cedric dumoulin |
| * |
| * @param <T> |
| * The type of the objects contained in the list. The type should be |
| * the same as the type of the values in the backuped Map. |
| */ |
| public class PropertyIndexedList<T> extends EObjectResolvingEList<T> { |
| |
| |
| /** |
| * |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * The backuped Map. The implementation should be an Observable Map from EMF. |
| * |
| */ |
| protected EMap<String, T> map; |
| |
| /** |
| * The ordered list of Property, used to indexing this List. |
| * The Property::index should be set. |
| */ |
| protected List<Property> propertyList; |
| |
| /** |
| * The classtype of the owner of the map |
| */ |
| protected Class<?> mapParentClasstype; |
| |
| /** |
| * The id of the map property in its parent. |
| */ |
| final protected int MAP_FEATURE_ID; |
| |
| /** |
| * The value that is used as a "null value". |
| * We can't use 'null' because the list don't support it (even |
| * if we set canSupportNull to true). |
| */ |
| final protected T NULL_VALUE; |
| |
| /** |
| * Constructor. |
| * |
| * @param dataClass |
| * Type of the element of the list |
| * @param layer |
| * The owner of this list and of the map |
| * @param featureID |
| * The feature Id of this list in the owner |
| * |
| * @param mapFeatureID |
| * The map feature id in the owner |
| */ |
| public PropertyIndexedList(EMap<String, T> map, Class<T> dataClass, InternalEObject layer, int featureID, int mapFeatureID, T nullValue) { |
| super(dataClass, layer, featureID); |
| MAP_FEATURE_ID = mapFeatureID; |
| NULL_VALUE = nullValue; |
| mapParentClasstype = layer.getClass(); |
| this.map = map; |
| init(); |
| } |
| |
| /** |
| * Constructor. |
| * |
| * @param dataClass |
| * Type of the element of the list |
| * @param layer |
| * The owner of this list and of the map |
| * @param featureID |
| * The feature Id of this list in the owner |
| * |
| * @param mapFeatureID |
| * The map feature id in the owner |
| */ |
| public PropertyIndexedList(Map<String, T> map, Class<T> dataClass, InternalEObject layer, int featureID, int mapFeatureID, T nullValue) { |
| super(dataClass, layer, featureID); |
| MAP_FEATURE_ID = mapFeatureID; |
| NULL_VALUE = nullValue; |
| mapParentClasstype = layer.getClass(); |
| this.map = (EMap<String, T>) map; |
| init(); |
| } |
| |
| /** |
| * Listen on the map for addition/deletion. |
| * |
| */ |
| protected void init() { |
| getEObject().eAdapters().add(new PropertyValuesSynchronizer()); |
| |
| } |
| |
| /** |
| * Allows double |
| * |
| * @see org.eclipse.emf.ecore.util.EObjectEList#isUnique() |
| * |
| * @return |
| */ |
| // |
| @Override |
| protected boolean isUnique() { |
| return false; |
| } |
| |
| /** |
| * Allow null values in the list. |
| * |
| * @see org.eclipse.emf.ecore.util.EObjectEList#canContainNull() |
| * |
| * @return |
| */ |
| // @Override |
| // protected boolean canContainNull() { |
| // return true; |
| // } |
| |
| |
| /** |
| * @param propertyList |
| * the propertyList to set |
| */ |
| public void setPropertyList(List<Property> propertyList) { |
| this.propertyList = propertyList; |
| try { |
| resetListElements(); |
| } catch (BadStateException e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| /** |
| * Reset the elements of the list, according to the map and the |
| * propertyList. |
| * |
| * @throws BadStateException |
| * |
| */ |
| private void resetListElements() throws BadStateException { |
| // Reset list |
| this.clear(); |
| |
| // Check application |
| if (propertyList == null) { |
| // do not synchronize the list |
| return; |
| // throw new BadStateException("Property 'application' must be set for resetAllPropertyValuesFromRegistry() to work."); |
| } |
| |
| List<Property> availableProperties; |
| int size; |
| try { |
| availableProperties = propertyList; |
| size = availableProperties.size(); |
| } catch (NullPointerException e) { |
| throw new BadStateException("Property 'application.propertyRegistry' must be set for resetAllPropertyValuesFromRegistry() to work."); |
| } |
| |
| // initialize the list |
| for (int i = 0; i < size; i++) { |
| String propertyName = availableProperties.get(i).getName(); |
| // Add the value, or null if not found. This ensure |
| // That the list will have the correct size. |
| T value = map.get(propertyName); |
| if (value == null) { |
| value = NULL_VALUE; |
| } |
| this.add(value); |
| } |
| } |
| |
| /** |
| * Get an element by its property. |
| * |
| * @param property |
| * @return |
| * @throws NotFoundException |
| * If the index of the property is not found. |
| */ |
| public T get(Property property) throws NotFoundException { |
| try { |
| return get(property.getIndex()); |
| } catch (IndexOutOfBoundsException e) { |
| throw new NotFoundException("Can't get element for Property '" + property.getName() |
| + "'. ", e); |
| } |
| } |
| |
| /** |
| * Set the element for the specified Property. |
| * |
| * @param property |
| * @param ele |
| * @throws BadStateException |
| * When the associated map is not set. |
| */ |
| public void set(Property property, T value) throws BadStateException { |
| if (map == null) { |
| throw new BadStateException("Can't set element for Property '" + property.getName() |
| + "'. The associated map should be set first"); |
| |
| } |
| |
| // Set the element in the map |
| map.put(property.getName(), value); |
| } |
| |
| /** |
| * Synchronize the specified propertyName with the value in the {@link #propertyValues} list. |
| * |
| * @param propertyName |
| * @param value |
| * @throws NotFoundException |
| */ |
| protected void synchronizePropertyValue(String propertyName, T value) throws BadStateException, NotFoundException { |
| |
| if (propertyList == null) { |
| // silently fail |
| return; |
| } |
| |
| try { |
| int propertyIndex = getPropertyIndex(propertyName); |
| set(propertyIndex, value); |
| } catch (NullPointerException e) { |
| throw new BadStateException("propertyList should be set first."); |
| } |
| } |
| |
| |
| /** |
| * Get a Property by its name. |
| * Lookup in the {@link #propertyList} property. |
| * |
| * @param propertyName |
| * @return |
| * @throws NotFoundException |
| */ |
| protected int getPropertyIndex(String propertyName) throws NotFoundException { |
| |
| if (propertyName == null) { |
| throw new NotFoundException("Null name not Allowed"); |
| } |
| for (int i = 0; i < propertyList.size(); i++) { |
| if (propertyName.equals(propertyList.get(i).getName())) { |
| return i; |
| } |
| } |
| |
| // Not found |
| throw new NotFoundException("No property found under name '" + propertyName + "'"); |
| |
| |
| } |
| |
| /** |
| * This class listen to #propertyValueMap, and synchronize propertyValues accordingly. |
| * |
| * This adapter listen on the map's parent to know if a Property key is |
| * added or removed. |
| * |
| */ |
| public class PropertyValuesSynchronizer extends AdapterImpl { |
| |
| |
| @Override |
| public void notifyChanged(Notification msg) { |
| if (log.isDebugEnabled()) { |
| log.debug("event " + msg.getEventType()); |
| } |
| |
| // Check if the notification comes from the map |
| if (msg.getFeatureID(mapParentClasstype) == MAP_FEATURE_ID) { |
| notifyLayerPropertyValueMapChanged(msg); |
| } |
| } |
| |
| /** |
| * The {@link PropertyIndexedList#map} has changed. Synchronize this list. |
| * |
| * @param msg |
| */ |
| protected void notifyLayerPropertyValueMapChanged(Notification msg) { |
| if (log.isDebugEnabled()) { |
| log.debug("map changed " + msg.getEventType()); |
| } |
| |
| switch (msg.getEventType()) { |
| case Notification.SET: { |
| // A key is modified |
| // Add the value to other list |
| @SuppressWarnings("unchecked") |
| Map.Entry<String, T> entry = (Map.Entry<String, T>) msg.getNewValue(); |
| |
| if (log.isDebugEnabled()) { |
| log.debug("SET - newValue=" + entry.getValue() |
| + ", key=" + entry.getKey()); |
| } |
| |
| // String newKey = (String)msg.getNewValue(); |
| T value = entry.getValue(); |
| if (value != null) { |
| try { |
| synchronizePropertyValue(entry.getKey(), value); |
| // synchronizePropertyValue(newKey, value); |
| } catch (BadStateException e) { |
| // Show error for debug |
| e.printStackTrace(); |
| } catch (NotFoundException e) { |
| // Show error for debug |
| e.printStackTrace(); |
| } |
| } |
| |
| |
| break; |
| } |
| case Notification.UNSET: |
| // A key is added |
| break; |
| case Notification.ADD: { |
| // An entry is added |
| @SuppressWarnings("unchecked") |
| Map.Entry<String, T> entry = (Map.Entry<String, T>) msg.getNewValue(); |
| if (log.isDebugEnabled()) { |
| log.debug("ADD - newValue=" + entry.getValue() |
| + ", key=" + entry.getKey()); |
| } |
| |
| // Add the corresponding instance to propertyValues |
| try { |
| synchronizePropertyValue(entry.getKey(), entry.getValue()); |
| } catch (LayersException e) { |
| // should not happen |
| e.printStackTrace(); |
| } |
| break; |
| } |
| case Notification.REMOVE: { |
| // An entry is removed |
| @SuppressWarnings("unchecked") |
| Map.Entry<String, T> entry = (Map.Entry<String, T>) msg.getNewValue(); |
| if (log.isDebugEnabled()) { |
| log.debug("REMOVE" + entry.getValue() |
| + ", key=" + entry.getKey()); |
| } |
| |
| // Add the corresponding instance to propertyValues |
| try { |
| synchronizePropertyValue(entry.getKey(), NULL_VALUE); |
| } catch (LayersException e) { |
| // should not happen |
| e.printStackTrace(); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| } |
| |
| } |
| |
| } |