/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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 com.vaadin.v7.data.util;
import java.util.Collection;
import java.util.Collections;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import com.vaadin.data.Binder;
import com.vaadin.data.provider.DataProvider;
import com.vaadin.v7.data.Item;
import com.vaadin.v7.data.Property;
/**
* Class for handling a set of identified Properties. The elements contained in
* a <code>MapItem</code> can be referenced using locally unique identifiers.
* The class supports listeners who are interested in changes to the Property
* set managed by the class.
*
* @author Vaadin Ltd.
* @since 3.0
*
* @deprecated As of 8.0, no direct replacement available. You can use {@link Map} directly as an item for {@link Binder}
* or {@link DataProvider} and access item properties with lambdas like {@code binder.forField(component).bind(...)} or
* {@code new Grid<Map<...>>(dataProvider).addColumn(map->map.get(...))}.
*/
@Deprecated
@SuppressWarnings("serial")
public class PropertysetItem
implements Item, Item.PropertySetChangeNotifier, Cloneable {
/* Private representation of the item */
/**
* Mapping from property id to property.
*/
private HashMap<Object, Property<?>> map = new HashMap<Object, Property<?>>();
/**
* List of all property ids to maintain the order.
*/
private LinkedList<Object> list = new LinkedList<Object>();
/**
* List of property set modification listeners.
*/
private LinkedList<Item.PropertySetChangeListener> propertySetChangeListeners = null;
/* Item methods */
/**
* Gets the Property corresponding to the given Property ID stored in the
* Item. If the Item does not contain the Property, <code>null</code> is
* returned.
*
* @param id
* the identifier of the Property to get.
* @return the Property with the given ID or <code>null</code>
*/
@Override
public Property getItemProperty(Object id) {
return map.get(id);
}
/**
* Gets the collection of IDs of all Properties stored in the Item.
*
* @return unmodifiable collection containing IDs of the Properties stored
* the Item
*/
@Override
public Collection<?> getItemPropertyIds() {
return Collections.unmodifiableCollection(list);
}
/* Item.Managed methods */
/**
* Removes the Property identified by ID from the Item. This functionality
* is optional. If the method is not implemented, the method always returns
* <code>false</code>.
*
* @param id
* the ID of the Property to be removed.
* @return <code>true</code> if the operation succeeded <code>false</code>
* if not
*/
@Override
public boolean removeItemProperty(Object id) {
// Cant remove missing properties
if (map.remove(id) == null) {
return false;
}
list.remove(id);
// Send change events
fireItemPropertySetChange();
return true;
}
/**
* Tries to add a new Property into the Item.
*
* @param id
* the ID of the new Property.
* @param property
* the Property to be added and associated with the id.
* @return <code>true</code> if the operation succeeded, <code>false</code>
* if not
*/
@Override
public boolean addItemProperty(Object id, Property property) {
// Null ids are not accepted
if (id == null) {
throw new NullPointerException("Item property id can not be null");
}
// Cant add a property twice
if (map.containsKey(id)) {
return false;
}
// Put the property to map
map.put(id, property);
list.add(id);
// Send event
fireItemPropertySetChange();
return true;
}
/**
* Gets the <code>String</code> representation of the contents of the Item.
* The format of the string is a space separated catenation of the
* <code>String</code> representations of the Properties contained by the
* Item.
*
* @return <code>String</code> representation of the Item contents
*/
@Override
public String toString() {
String retValue = "";
for (final Iterator<?> i = getItemPropertyIds().iterator(); i
.hasNext();) {
final Object propertyId = i.next();
retValue += getItemProperty(propertyId).getValue();
if (i.hasNext()) {
retValue += " ";
}
}
return retValue;
}
/* Notifiers */
/**
* An <code>event</code> object specifying an Item whose Property set has
* changed.
*
* @author Vaadin Ltd.
* @since 3.0
*/
private static class PropertySetChangeEvent extends EventObject
implements Item.PropertySetChangeEvent {
private PropertySetChangeEvent(Item source) {
super(source);
}
/**
* Gets the Item whose Property set has changed.
*
* @return source object of the event as an <code>Item</code>
*/
@Override
public Item getItem() {
return (Item) getSource();
}
}
/**
* Registers a new property set change listener for this Item.
*
* @param listener
* the new Listener to be registered.
*/
@Override
public void addPropertySetChangeListener(
Item.PropertySetChangeListener listener) {
if (propertySetChangeListeners == null) {
propertySetChangeListeners = new LinkedList<PropertySetChangeListener>();
}
propertySetChangeListeners.add(listener);
}
/**
* @deprecated As of 7.0, replaced by
* {@link #addPropertySetChangeListener(Item.PropertySetChangeListener)}
**/
@Override
@Deprecated
public void addListener(Item.PropertySetChangeListener listener) {
addPropertySetChangeListener(listener);
}
/**
* Removes a previously registered property set change listener.
*
* @param listener
* the Listener to be removed.
*/
@Override
public void removePropertySetChangeListener(
Item.PropertySetChangeListener listener) {
if (propertySetChangeListeners != null) {
propertySetChangeListeners.remove(listener);
}
}
/**
* @deprecated As of 7.0, replaced by
* {@link #removePropertySetChangeListener(Item.PropertySetChangeListener)}
**/
@Override
@Deprecated
public void removeListener(Item.PropertySetChangeListener listener) {
removePropertySetChangeListener(listener);
}
/**
* Sends a Property set change event to all interested listeners.
*/
private void fireItemPropertySetChange() {
if (propertySetChangeListeners != null) {
final Object[] l = propertySetChangeListeners.toArray();
final Item.PropertySetChangeEvent event = new PropertysetItem.PropertySetChangeEvent(
this);
for (int i = 0; i < l.length; i++) {
((Item.PropertySetChangeListener) l[i])
.itemPropertySetChange(event);
}
}
}
public Collection<?> getListeners(Class<?> eventType) {
if (Item.PropertySetChangeEvent.class.isAssignableFrom(eventType)) {
if (propertySetChangeListeners == null) {
return Collections.EMPTY_LIST;
} else {
return Collections
.unmodifiableCollection(propertySetChangeListeners);
}
}
return Collections.EMPTY_LIST;
}
/**
* Creates and returns a copy of this object.
* <p>
* The method <code>clone</code> performs a shallow copy of the
* <code>PropertysetItem</code>.
* </p>
* <p>
* Note : All arrays are considered to implement the interface Cloneable.
* Otherwise, this method creates a new instance of the class of this object
* and initializes all its fields with exactly the contents of the
* corresponding fields of this object, as if by assignment, the contents of
* the fields are not themselves cloned. Thus, this method performs a
* "shallow copy" of this object, not a "deep copy" operation.
* </p>
*
* @throws CloneNotSupportedException
* if the object's class does not support the Cloneable
* interface.
*
* @see java.lang.Object#clone()
*/
@Override
public Object clone() throws CloneNotSupportedException {
final PropertysetItem npsi = new PropertysetItem();
npsi.list = list != null ? (LinkedList<Object>) list.clone() : null;
npsi.propertySetChangeListeners = propertySetChangeListeners != null
? (LinkedList<PropertySetChangeListener>) propertySetChangeListeners
.clone()
: null;
npsi.map = (HashMap<Object, Property<?>>) map.clone();
return npsi;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof PropertysetItem)) {
return false;
}
final PropertysetItem other = (PropertysetItem) obj;
if (other.list != list) {
if (other.list == null) {
return false;
}
if (!other.list.equals(list)) {
return false;
}
}
if (other.map != map) {
if (other.map == null) {
return false;
}
if (!other.map.equals(map)) {
return false;
}
}
if (other.propertySetChangeListeners != propertySetChangeListeners) {
boolean thisEmpty = (propertySetChangeListeners == null
|| propertySetChangeListeners.isEmpty());
boolean otherEmpty = (other.propertySetChangeListeners == null
|| other.propertySetChangeListeners.isEmpty());
if (thisEmpty && otherEmpty) {
return true;
}
if (otherEmpty) {
return false;
}
if (!other.propertySetChangeListeners
.equals(propertySetChangeListeners)) {
return false;
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return (list == null ? 0 : list.hashCode())
^ (map == null ? 0 : map.hashCode())
^ ((propertySetChangeListeners == null
|| propertySetChangeListeners.isEmpty()) ? 0
: propertySetChangeListeners.hashCode());
}
}