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    
024    package microsoft.exchange.webservices.data.core;
025    
026    import microsoft.exchange.webservices.data.misc.OutParam;
027    import microsoft.exchange.webservices.data.property.complex.IPropertyBagChangedDelegate;
028    
029    import java.util.ArrayList;
030    import java.util.HashMap;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.Map;
034    
035    /**
036     * Represents a simple property bag.
037     *
038     * @param <TKey> The type of key
039     */
040    public 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. <value>The added item.</value>
102       *
103       * @return the added item
104       */
105      public Iterable<TKey> getAddedItems() {
106        return this.addedItems;
107      }
108    
109      /**
110       * Gets the removed item. <value>The removed item.</value>
111       *
112       * @return the removed item
113       */
114      public Iterable<TKey> getRemovedItems() {
115        return this.removedItems;
116      }
117    
118      /**
119       * Gets the modified item. <value>The modified item.</value>
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    }