001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.api;
028
029import java.util.AbstractMap;
030import java.util.AbstractSet;
031import java.util.Collection;
032import java.util.HashMap;
033import java.util.Iterator;
034import java.util.Map;
035import java.util.NoSuchElementException;
036import java.util.Set;
037
038import org.opends.server.types.DN;
039
040/**
041 * The DITCacheMap class implements custom Map for structural storage of
042 * arbitrary objects in Directory Information Tree (DIT) like structure.
043 * <p>
044 * This Map intended usage is for caching various server objects which can be
045 * subject to subtree operations like retrieval or removal of all objects under
046 * a specific DN. While using a regular Map it would require the entire Map
047 * iteration to achieve, this Map implementation maintains such internal
048 * structure that subtree operations are more efficient and do not require
049 * iterations over the entire map, instead additional subtree operations methods
050 * are provided by this Map to do just that.
051 * <p>
052 * API wise it behaves exactly like a regular Map implementation except for
053 * providing additional subtree methods. All required linkage and structuring is
054 * performed within this Map implementation itself and not exposed via the API
055 * in any way. For example, putting these key/value pairs:
056 *
057 * <pre>
058 * cn=Object1,ou=Objects,dc=example,dc=com : object1
059 * cn=Object2,ou=Objects,dc=example,dc=com : object2
060 * cn=Object3,ou=Objects,dc=example,dc=com : object3
061 * </pre>
062 *
063 * then invoking a subtree method on this Map with any of these keys:
064 *
065 * <pre>
066 * ou=Objects,dc=example,dc=com
067 * dc=example,dc=com
068 * dc=com
069 * </pre>
070 *
071 * would bring all three objects previously stored in this map into subtree
072 * operation scope. Standard Map API methods can only work with the objects
073 * previously stored in this map explicitly.
074 * <p>
075 * Note that this Map implementation is not synchronized.
076 *
077 * @param <T>
078 *          arbitrary object type.
079 */
080public final class DITCacheMap<T> extends AbstractMap<DN,T>
081{
082  /**
083   * Node class for object storage and
084   * linking to any subordinate nodes.
085   * @param <T> arbitrary storage object.
086   */
087  private static final class Node<T>
088  {
089    /** Node DN. */
090    DN dn;
091    /**
092     * Storage object or null if this node exist
093     * only to support the DIT like structuring.
094     */
095    T element;
096    /** Parent. */
097    Node<T> parent;
098    /** First child. */
099    Node<T> child;
100    /** Next sibling. */
101    Node<T> next;
102    /** Previous sibling. */
103    Node<T> previous;
104
105    /** {@inheritDoc} */
106    @Override
107    public String toString()
108    {
109      if (element != null)
110      {
111        return "node(" + element + ")";
112      }
113      else
114      {
115        return "glue";
116      }
117    }
118  }
119
120  /** Map size reflecting only nodes containing non empty elements. */
121  private int size;
122
123  /** Backing Map implementation. */
124  private final Map<DN,Node<T>> ditCacheMap = new HashMap<>();
125
126  /** Default constructor. */
127  public DITCacheMap()
128  {
129  }
130
131  /**
132   * Constructs a new DITCacheMap from a given Map.
133   * @param m existing Map to construct new
134   *          DITCacheMap from.
135   */
136  public DITCacheMap(Map<? extends DN, ? extends T> m)
137  {
138    this.putAll(m);
139  }
140
141  /** {@inheritDoc} */
142  @Override
143  public int size()
144  {
145    return size;
146  }
147
148  /** {@inheritDoc} */
149  @Override
150  public boolean isEmpty()
151  {
152    return ditCacheMap.isEmpty();
153  }
154
155  /** {@inheritDoc} */
156  @Override
157  public boolean containsKey(Object key)
158  {
159    return get(key) != null;
160  }
161
162  /** {@inheritDoc} */
163  @Override
164  public boolean containsValue(Object value)
165  {
166    for (Node<T> node : ditCacheMap.values())
167    {
168      if (node.element != null && node.element.equals(value))
169      {
170        return true;
171      }
172    }
173    return false;
174  }
175
176  /** {@inheritDoc} */
177  @Override
178  public T get(Object key)
179  {
180    Node<T> node = ditCacheMap.get(key);
181    return node != null ? node.element : null;
182  }
183
184  /**
185   * Returns a set of stored objects
186   * subordinate to subtree DN.
187   * @param key subtree DN.
188   * @return collection of stored objects
189   *         subordinate to subtree DN.
190   */
191  public Collection<T> getSubtree(DN key)
192  {
193    return new DITSubtreeSet(key);
194  }
195
196  /** {@inheritDoc} */
197  @Override
198  public T put(DN key, T value)
199  {
200    final Node<T> existingNode = ditCacheMap.get(key);
201    if (existingNode != null)
202    {
203      final T returnValue = existingNode.element;
204      existingNode.element = value;
205      return returnValue;
206    }
207
208    Node<T> node = new Node<>();
209    node.dn = key;
210    node.element = value;
211    node.parent = null;
212    node.child = null;
213    node.next = null;
214    node.previous = null;
215
216    ditCacheMap.put(key, node);
217    size++;
218
219    // Update parent hierarchy.
220    for (DN parentDN = key.parent();
221         parentDN != null;
222         parentDN = parentDN.parent())
223    {
224      final Node<T> parentNode = ditCacheMap.get(parentDN);
225
226      if (parentNode == null)
227      {
228        // Add glue node.
229        final Node<T> newParentNode = new Node<>();
230        newParentNode.dn = parentDN;
231        newParentNode.element = null;
232        newParentNode.parent = null;
233        newParentNode.child = node;
234        newParentNode.next = null;
235        newParentNode.previous = null;
236        ditCacheMap.put(parentDN, newParentNode);
237        node.parent = newParentNode;
238        node = newParentNode;
239      }
240      else
241      {
242        if (parentNode.child != null)
243        {
244          Node<T> lastNode = parentNode.child;
245          while (lastNode.next != null)
246          {
247            lastNode = lastNode.next;
248          }
249          node.previous = lastNode;
250          lastNode.next = node;
251        }
252        else
253        {
254          parentNode.child = node;
255        }
256        node.parent = parentNode;
257        break;
258      }
259    }
260    return null;
261  }
262
263  /** {@inheritDoc} */
264  @Override
265  public T remove(Object key)
266  {
267    final DN dn = (DN) key;
268    final Node<T> node = ditCacheMap.get(dn);
269    if (node == null)
270    {
271      return null;
272    }
273
274    final T returnValue = node.element;
275    if (returnValue == null)
276    {
277      return null;
278    }
279
280    // Remove element from DIT.
281    size--;
282    node.element = null;
283
284    if (node.child == null)
285    {
286      // This node is now glue, so remove it completely and fix up
287      // any other nodes which reference it.
288      ditCacheMap.remove(dn);
289      fixNodeReferences(node);
290    }
291
292    return returnValue;
293  }
294
295
296
297  /**
298   * Remove references to a node after it has been removed.
299   * @param node The node which has just been removed.
300   */
301  private void fixNodeReferences(Node<T> node)
302  {
303    while (true)
304    {
305      // Fix siblings.
306      final Node<T> nextNode = node.next;
307      final Node<T> previousNode = node.previous;
308
309      if (nextNode != null)
310      {
311        nextNode.previous = previousNode;
312      }
313
314      if (previousNode != null)
315      {
316        previousNode.next = nextNode;
317      }
318
319      // Fix parent, if it exists.
320      final Node<T> parentNode = node.parent;
321      if (parentNode == null)
322      {
323        // Reached the top of the DIT, so no parents to fix.
324        break;
325      }
326
327      if (parentNode.child != node)
328      {
329        // The parent references another sibling and does not need fixing.
330        break;
331      }
332
333      if (nextNode != null)
334      {
335        // Update child pointer and return.
336        parentNode.child = nextNode;
337        break;
338      }
339
340      if (parentNode.element != null)
341      {
342        // Parent node is still needed as it contains data.
343        break;
344      }
345
346      // The parent node is glue so remove it.
347      ditCacheMap.remove(parentNode.dn);
348
349      // Smash pointers.
350      node.parent = null;
351      node.previous = null;
352      node.next = null;
353
354      // Continue up tree.
355      node = parentNode;
356    }
357  }
358
359
360
361  /**
362   * Removes a set of stored objects subordinate to subtree DN.
363   * @param key subtree DN.
364   * @param values collection for removed objects subordinate
365   *               to subtree DN or <code>null</code>.
366   * @return <code>true</code> on success or <code>false</code> otherwise.
367   */
368  public boolean removeSubtree(DN key, Collection<? super T> values)
369  {
370    final Node<T> node = ditCacheMap.remove(key);
371    if (node != null)
372    {
373      // Fix up sibling and parent references.
374      fixNodeReferences(node);
375
376      // Collect all elements and update the size.
377      adjustSizeAndCollectElements(node, values);
378      return true;
379    }
380    return false;
381  }
382
383
384
385  /**
386   * Iterate through detached subtree counting and collecting any elements.
387   *
388   * @param node
389   *          The node to be collected.
390   * @param values
391   *          Collection in which to put elements.
392   */
393  private void adjustSizeAndCollectElements(final Node<T> node,
394      Collection<? super T> values)
395  {
396    if (node.element != null)
397    {
398      if (values != null)
399      {
400        values.add(node.element);
401      }
402      node.element = null;
403      size--;
404    }
405
406    // Repeat for each child.
407    Node<T> child = node.child;
408    while (child != null)
409    {
410      final Node<T> next = child.next;
411      adjustSizeAndCollectElements(child, values);
412      child = next;
413    }
414
415    // Smash pointers.
416    node.parent = null;
417    node.child = null;
418    node.previous = null;
419    node.next = null;
420
421    // Remove node from map.
422    ditCacheMap.remove(node.dn);
423  }
424
425
426
427  /** {@inheritDoc} */
428  @Override
429  public void putAll(Map<? extends DN, ? extends T> m)
430  {
431    for (Entry<? extends DN, ? extends T> entry : m.entrySet())
432    {
433      put(entry.getKey(), entry.getValue());
434    }
435  }
436
437  /** {@inheritDoc} */
438  @Override
439  public void clear()
440  {
441    ditCacheMap.clear();
442    size = 0;
443  }
444
445  /** {@inheritDoc} */
446  @Override
447  public Set<Entry<DN, T>> entrySet()
448  {
449    return new DITCacheEntrySet();
450  }
451
452  /** EntrySet class implementation for the DITCacheMap. */
453  private class DITCacheEntrySet extends AbstractSet<Entry<DN, T>>
454  {
455    /** Iterator class implementation for the DITCacheEntrySet. */
456    private class EntryIterator implements Iterator<Entry<DN, T>>
457    {
458      private Iterator<Entry<DN, Node<T>>> ditCacheMapIterator;
459      private Entry<DN, Node<T>> currentEntry;
460      private Entry<DN, Node<T>> nextEntry;
461      private boolean hasNext;
462
463      /** Default constructor. */
464      public EntryIterator()
465      {
466        ditCacheMapIterator = ditCacheMap.entrySet().iterator();
467        currentEntry = null;
468        nextEntry = null;
469        hasNext = false;
470      }
471
472      /** {@inheritDoc} */
473      @Override
474      public boolean hasNext()
475      {
476        if (hasNext)
477        {
478          return true;
479        }
480        while (ditCacheMapIterator.hasNext())
481        {
482          Entry<DN, Node<T>> entry = ditCacheMapIterator.next();
483          Node<T> node = entry.getValue();
484          if (node != null && node.element != null)
485          {
486            nextEntry = entry;
487            hasNext = true;
488            return true;
489          }
490        }
491        nextEntry = null;
492        return false;
493      }
494
495      /** {@inheritDoc} */
496      @Override
497      public Entry<DN, T> next()
498      {
499        if (nextEntry != null)
500        {
501          Node<T> node = nextEntry.getValue();
502          currentEntry = nextEntry;
503          nextEntry = null;
504          hasNext = false;
505          return new DITCacheMapEntry(node.dn, node.element);
506        }
507        while (ditCacheMapIterator.hasNext())
508        {
509          Entry<DN, Node<T>> entry = ditCacheMapIterator.next();
510          Node<T> node = entry.getValue();
511          if (node != null && node.element != null)
512          {
513            currentEntry = entry;
514            hasNext = false;
515            return new DITCacheMapEntry(node.dn, node.element);
516          }
517        }
518        throw new NoSuchElementException();
519      }
520
521      /** {@inheritDoc} */
522      @Override
523      public void remove()
524      {
525        if (currentEntry != null)
526        {
527          Entry<DN, Node<T>> oldIteratorEntry = null;
528          if (hasNext())
529          {
530            oldIteratorEntry = nextEntry;
531          }
532          if (DITCacheMap.this.remove(currentEntry.getKey()) != null)
533          {
534            ditCacheMapIterator = ditCacheMap.entrySet().iterator();
535            currentEntry = null;
536            nextEntry = null;
537            hasNext = false;
538            while (hasNext())
539            {
540              Entry<DN, T> newIteratorEntry = next();
541              if (oldIteratorEntry != null
542                  && oldIteratorEntry.getKey().equals(newIteratorEntry.getKey())
543                  && oldIteratorEntry.getValue().element.equals(newIteratorEntry.getValue()))
544              {
545                nextEntry = currentEntry;
546                hasNext = true;
547                return;
548              }
549            }
550            currentEntry = null;
551            nextEntry = null;
552            hasNext = false;
553            return;
554          }
555        }
556        throw new IllegalStateException();
557      }
558    }
559
560    /** {@inheritDoc} */
561    @Override
562    public int size()
563    {
564      return DITCacheMap.this.size();
565    }
566
567    /** {@inheritDoc} */
568    @Override
569    public Iterator<Entry<DN, T>> iterator()
570    {
571      return new EntryIterator();
572    }
573  }
574
575  /** Map.Entry class implementation for the DITCacheMap. */
576  private class DITCacheMapEntry implements Map.Entry<DN, T>
577  {
578    private DN key;
579    private T  value;
580
581    /**
582     * Constructs a new DITCacheMapEntry
583     * with given key and value.
584     * @param key Map.Entry key.
585     * @param value Map.Entry value.
586     */
587    public DITCacheMapEntry(DN key, T value)
588    {
589      this.key = key;
590      this.value = value;
591    }
592
593    /** {@inheritDoc} */
594    @Override
595    public DN getKey()
596    {
597      return key;
598    }
599
600    /** {@inheritDoc} */
601    @Override
602    public T getValue()
603    {
604      return value;
605    }
606
607    /** {@inheritDoc} */
608    @Override
609    public T setValue(T value)
610    {
611      Node<T> node = ditCacheMap.get(key);
612      T oldValue = this.value;
613      node.element = value;
614      this.value = value;
615      return oldValue;
616    }
617  }
618
619  /** SubtreeSet class implementation. */
620  private class DITSubtreeSet extends AbstractSet<T>
621  {
622    /** Set key. */
623    private DN key;
624
625    /**
626     * Keyed constructor.
627     * @param key to construct
628     *        this set from.
629     */
630    public DITSubtreeSet(DN key)
631    {
632      this.key = key;
633    }
634
635    /** Iterator class implementation for SubtreeSet. */
636    private class SubtreeSetIterator implements Iterator<T>
637    {
638      /** Iterator key. */
639      private DN key;
640
641      /** Iterator root node. */
642      private Node<T> rootNode;
643
644      /** Iterator current node. */
645      private Node<T> node;
646
647      /** Default constructor. */
648      public SubtreeSetIterator()
649      {
650        this.key = DITSubtreeSet.this.key;
651        rootNode = ditCacheMap.get(this.key);
652        node = rootNode;
653      }
654
655      /**
656       * Keyed constructor.
657       * @param key to cue this
658       *        iterator from.
659       */
660      public SubtreeSetIterator(DN key)
661      {
662        this.key = key;
663        rootNode = ditCacheMap.get(this.key);
664        node = rootNode;
665      }
666
667      /** {@inheritDoc} */
668      @Override
669      public boolean hasNext()
670      {
671        if (rootNode != null)
672        {
673          if (node == rootNode && rootNode.element != null)
674          {
675            return true;
676          }
677          while (node != null)
678          {
679            if (node.element != null)
680            {
681              return true;
682            }
683            if (node.child != null)
684            {
685              node = node.child;
686            }
687            else
688            {
689              while (node.next == null && node.parent != rootNode)
690              {
691                node = node.parent;
692              }
693              node = node.next;
694            }
695          }
696        }
697        return false;
698      }
699
700      /** {@inheritDoc} */
701      @Override
702      public T next()
703      {
704        T element = null;
705
706        if (rootNode != null)
707        {
708          if (node == rootNode)
709          {
710            node = rootNode.child;
711            if (rootNode.element != null)
712            {
713              return rootNode.element;
714            }
715          }
716          while (node != null)
717          {
718            element = node.element;
719            if (node.child != null)
720            {
721              node = node.child;
722            }
723            else
724            {
725              while (node.next == null && node.parent != rootNode)
726              {
727                node = node.parent;
728              }
729              node = node.next;
730            }
731            if (element != null)
732            {
733              return element;
734            }
735          }
736        }
737        throw new NoSuchElementException();
738      }
739
740      /** {@inheritDoc} */
741      @Override
742      public void remove()
743      {
744        throw new UnsupportedOperationException();
745      }
746    }
747
748    /** {@inheritDoc} */
749    @Override
750    public Iterator<T> iterator()
751    {
752      return new SubtreeSetIterator();
753    }
754
755    /** {@inheritDoc} */
756    @Override
757    public int size()
758    {
759      int size = 0;
760
761      Iterator<T> iterator = new SubtreeSetIterator(this.key);
762      while (iterator.hasNext())
763      {
764        iterator.next();
765        size++;
766      }
767
768      return size;
769    }
770  }
771
772  /**
773   * Returns the size of the internal map. Used for testing purposes only.
774   *
775   * @return The size of the internal map.
776   */
777  int getMapSize()
778  {
779    return ditCacheMap.size();
780  }
781}