001/*
002 * The MIT License
003 * Copyright (c) 2012 Microsoft Corporation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a copy
006 * of this software and associated documentation files (the "Software"), to deal
007 * in the Software without restriction, including without limitation the rights
008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009 * copies of the Software, and to permit persons to whom the Software is
010 * furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021 * THE SOFTWARE.
022 */
023
024package microsoft.exchange.webservices.data.core;
025
026import microsoft.exchange.webservices.data.misc.OutParam;
027import microsoft.exchange.webservices.data.property.complex.IPropertyBagChangedDelegate;
028
029import java.util.ArrayList;
030import java.util.HashMap;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Map;
034
035/**
036 * Represents a simple property bag.
037 *
038 * @param <TKey> The type of key
039 */
040public class SimplePropertyBag<TKey> implements Iterable<HashMap<TKey, Object>> {
041
042  /**
043   * The item.
044   */
045  private Map<TKey, Object> items = new HashMap<TKey, Object>();
046
047  /**
048   * The removed item.
049   */
050  private List<TKey> removedItems = new ArrayList<TKey>();
051
052  /**
053   * The added item.
054   */
055  private List<TKey> addedItems = new ArrayList<TKey>();
056
057  /**
058   * The modified item.
059   */
060  private List<TKey> modifiedItems = new ArrayList<TKey>();
061
062  /**
063   * Add item to change list.
064   *
065   * @param key        the key
066   * @param changeList the change list
067   */
068  private void internalAddItemToChangeList(TKey key, List<TKey> changeList) {
069    if (!changeList.contains(key)) {
070      changeList.add(key);
071    }
072  }
073
074  /**
075   * Triggers dispatch of the change event.
076   */
077  private void changed() {
078    if (!onChange.isEmpty()) {
079      for (IPropertyBagChangedDelegate<TKey> change : onChange) {
080        change.propertyBagChanged(this);
081      }
082    }
083  }
084
085  /**
086   * Remove item.
087   *
088   * @param key the key
089   */
090  private void internalRemoveItem(TKey key) {
091    OutParam<Object> value = new OutParam<Object>();
092    if (this.tryGetValue(key, value)) {
093      this.items.remove(key);
094      this.removedItems.add(key);
095      this.changed();
096    }
097  }
098
099
100  /**
101   * Gets the added item.
102   *
103   * @return the added item
104   */
105  public Iterable<TKey> getAddedItems() {
106    return this.addedItems;
107  }
108
109  /**
110   * Gets the removed item.
111   *
112   * @return the removed item
113   */
114  public Iterable<TKey> getRemovedItems() {
115    return this.removedItems;
116  }
117
118  /**
119   * Gets the modified item.
120   *
121   * @return the modified item
122   */
123  public Iterable<TKey> getModifiedItems() {
124    return this.modifiedItems;
125  }
126
127  /**
128   * Initializes a new instance of the class.
129   */
130  public SimplePropertyBag() {
131  }
132
133  /**
134   * Clears the change log.
135   */
136  public void clearChangeLog() {
137    this.removedItems.clear();
138    this.addedItems.clear();
139    this.modifiedItems.clear();
140  }
141
142  /**
143   * Determines whether the specified key is in the property bag.
144   *
145   * @param key the key
146   * @return true, if successful if the specified key exists; otherwise, .
147   */
148  public boolean containsKey(TKey key) {
149    return this.items.containsKey(key);
150  }
151
152  /**
153   * Tries to get value.
154   *
155   * @param key   the key
156   * @param value the value
157   * @return True if value exists in property bag.
158   */
159  public boolean tryGetValue(TKey key, OutParam<Object> value) {
160    if (this.items.containsKey(key)) {
161      value.setParam(this.items.get(key));
162      return true;
163    } else {
164      value.setParam(null);
165      return false;
166    }
167  }
168
169  /**
170   * Gets the simple property bag.
171   *
172   * @param key the key
173   * @return the simple property bag
174   */
175  public Object getSimplePropertyBag(TKey key) {
176    OutParam<Object> value = new OutParam<Object>();
177    if (this.tryGetValue(key, value)) {
178      return value.getParam();
179    } else {
180      return null;
181    }
182  }
183
184  /**
185   * Sets the simple property bag.
186   *
187   * @param key   the key
188   * @param value the value
189   */
190  public void setSimplePropertyBag(TKey key, Object value) {
191    if (value == null) {
192      this.internalRemoveItem(key);
193    } else {
194      // If the item was to be deleted, the deletion becomes an update.
195      if (this.removedItems.remove(key)) {
196        internalAddItemToChangeList(key, this.modifiedItems);
197      } else {
198        // If the property value was not set, we have a newly set
199        // property.
200        if (!this.containsKey(key)) {
201          internalAddItemToChangeList(key, this.addedItems);
202        } else {
203          // The last case is that we have a modified property.
204          if (!this.modifiedItems.contains(key)) {
205            internalAddItemToChangeList(key, this.modifiedItems);
206          }
207        }
208      }
209
210      this.items.put(key, value);
211      this.changed();
212    }
213  }
214
215  /**
216   * Occurs when Changed.
217   */
218  private List<IPropertyBagChangedDelegate<TKey>> onChange =
219      new ArrayList<IPropertyBagChangedDelegate<TKey>>();
220
221  /**
222   * Set event to happen when property changed.
223   *
224   * @param change change event
225   */
226  public void addOnChangeEvent(IPropertyBagChangedDelegate<TKey> change) {
227    onChange.add(change);
228  }
229
230  /**
231   * Remove the event from happening when property changed.
232   *
233   * @param change change event
234   */
235  public void removeChangeEvent(IPropertyBagChangedDelegate<TKey> change) {
236    onChange.remove(change);
237  }
238
239  /**
240   * Returns an iterator over a set of elements of type T.
241   *
242   * @return an Iterator.
243   */
244  @Override
245  public Iterator<HashMap<TKey, Object>> iterator() {
246    return (Iterator<HashMap<TKey, Object>>) this.items.keySet().iterator();
247  }
248
249}