001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2016 ForgeRock AS.
016 */
017package org.opends.server.api;
018
019import java.util.AbstractMap;
020import java.util.AbstractSet;
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.Map;
025import java.util.NoSuchElementException;
026import java.util.Set;
027
028import org.forgerock.opendj.ldap.DN;
029
030/**
031 * The DITCacheMap class implements custom Map for structural storage of
032 * arbitrary objects in Directory Information Tree (DIT) like structure.
033 * <p>
034 * This Map intended usage is for caching various server objects which can be
035 * subject to subtree operations like retrieval or removal of all objects under
036 * a specific DN. While using a regular Map it would require the entire Map
037 * iteration to achieve, this Map implementation maintains such internal
038 * structure that subtree operations are more efficient and do not require
039 * iterations over the entire map, instead additional subtree operations methods
040 * are provided by this Map to do just that.
041 * <p>
042 * API wise it behaves exactly like a regular Map implementation except for
043 * providing additional subtree methods. All required linkage and structuring is
044 * performed within this Map implementation itself and not exposed via the API
045 * in any way. For example, putting these key/value pairs:
046 *
047 * <pre>
048 * cn=Object1,ou=Objects,dc=example,dc=com : object1
049 * cn=Object2,ou=Objects,dc=example,dc=com : object2
050 * cn=Object3,ou=Objects,dc=example,dc=com : object3
051 * </pre>
052 *
053 * then invoking a subtree method on this Map with any of these keys:
054 *
055 * <pre>
056 * ou=Objects,dc=example,dc=com
057 * dc=example,dc=com
058 * dc=com
059 * </pre>
060 *
061 * would bring all three objects previously stored in this map into subtree
062 * operation scope. Standard Map API methods can only work with the objects
063 * previously stored in this map explicitly.
064 * <p>
065 * Note that this Map implementation is not synchronized.
066 *
067 * @param <T>
068 *          arbitrary object type.
069 */
070public final class DITCacheMap<T> extends AbstractMap<DN,T>
071{
072  /**
073   * Node class for object storage and
074   * linking to any subordinate nodes.
075   * @param <T> arbitrary storage object.
076   */
077  private static final class Node<T>
078  {
079    /** Node DN. */
080    DN dn;
081    /**
082     * Storage object or null if this node exist
083     * only to support the DIT like structuring.
084     */
085    T element;
086    /** Parent. */
087    Node<T> parent;
088    /** First child. */
089    Node<T> child;
090    /** Next sibling. */
091    Node<T> next;
092    /** Previous sibling. */
093    Node<T> previous;
094
095    /** {@inheritDoc} */
096    @Override
097    public String toString()
098    {
099      if (element != null)
100      {
101        return "node(" + element + ")";
102      }
103      else
104      {
105        return "glue";
106      }
107    }
108  }
109
110  /** Map size reflecting only nodes containing non empty elements. */
111  private int size;
112
113  /** Backing Map implementation. */
114  private final Map<DN,Node<T>> ditCacheMap = new HashMap<>();
115
116  /** Default constructor. */
117  public DITCacheMap()
118  {
119  }
120
121  /**
122   * Constructs a new DITCacheMap from a given Map.
123   * @param m existing Map to construct new
124   *          DITCacheMap from.
125   */
126  public DITCacheMap(Map<? extends DN, ? extends T> m)
127  {
128    this.putAll(m);
129  }
130
131  /** {@inheritDoc} */
132  @Override
133  public int size()
134  {
135    return size;
136  }
137
138  /** {@inheritDoc} */
139  @Override
140  public boolean isEmpty()
141  {
142    return ditCacheMap.isEmpty();
143  }
144
145  /** {@inheritDoc} */
146  @Override
147  public boolean containsKey(Object key)
148  {
149    return get(key) != null;
150  }
151
152  /** {@inheritDoc} */
153  @Override
154  public boolean containsValue(Object value)
155  {
156    for (Node<T> node : ditCacheMap.values())
157    {
158      if (node.element != null && node.element.equals(value))
159      {
160        return true;
161      }
162    }
163    return false;
164  }
165
166  /** {@inheritDoc} */
167  @Override
168  public T get(Object key)
169  {
170    Node<T> node = ditCacheMap.get(key);
171    return node != null ? node.element : null;
172  }
173
174  /**
175   * Returns a set of stored objects subordinate to subtree DN.
176   * @param key subtree DN.
177   * @return collection of stored objects subordinate to subtree DN.
178   */
179  public Collection<T> getSubtree(DN key)
180  {
181    return new DITSubtreeSet(key);
182  }
183
184  /** {@inheritDoc} */
185  @Override
186  public T put(DN key, T value)
187  {
188    final Node<T> existingNode = ditCacheMap.get(key);
189    if (existingNode != null)
190    {
191      final T returnValue = existingNode.element;
192      existingNode.element = value;
193      return returnValue;
194    }
195
196    Node<T> node = new Node<>();
197    node.dn = key;
198    node.element = value;
199    node.parent = null;
200    node.child = null;
201    node.next = null;
202    node.previous = null;
203
204    ditCacheMap.put(key, node);
205    size++;
206
207    // Update parent hierarchy.
208    for (DN parentDN = key.parent();
209         parentDN != null;
210         parentDN = parentDN.parent())
211    {
212      final Node<T> parentNode = ditCacheMap.get(parentDN);
213
214      if (parentNode == null)
215      {
216        // Add glue node.
217        final Node<T> newParentNode = new Node<>();
218        newParentNode.dn = parentDN;
219        newParentNode.element = null;
220        newParentNode.parent = null;
221        newParentNode.child = node;
222        newParentNode.next = null;
223        newParentNode.previous = null;
224        ditCacheMap.put(parentDN, newParentNode);
225        node.parent = newParentNode;
226        node = newParentNode;
227      }
228      else
229      {
230        if (parentNode.child != null)
231        {
232          Node<T> lastNode = parentNode.child;
233          while (lastNode.next != null)
234          {
235            lastNode = lastNode.next;
236          }
237          node.previous = lastNode;
238          lastNode.next = node;
239        }
240        else
241        {
242          parentNode.child = node;
243        }
244        node.parent = parentNode;
245        break;
246      }
247    }
248    return null;
249  }
250
251  /** {@inheritDoc} */
252  @Override
253  public T remove(Object key)
254  {
255    final DN dn = (DN) key;
256    final Node<T> node = ditCacheMap.get(dn);
257    if (node == null)
258    {
259      return null;
260    }
261
262    final T returnValue = node.element;
263    if (returnValue == null)
264    {
265      return null;
266    }
267
268    // Remove element from DIT.
269    size--;
270    node.element = null;
271
272    if (node.child == null)
273    {
274      // This node is now glue, so remove it completely and fix up
275      // any other nodes which reference it.
276      ditCacheMap.remove(dn);
277      fixNodeReferences(node);
278    }
279
280    return returnValue;
281  }
282
283
284
285  /**
286   * Remove references to a node after it has been removed.
287   * @param node The node which has just been removed.
288   */
289  private void fixNodeReferences(Node<T> node)
290  {
291    while (true)
292    {
293      // Fix siblings.
294      final Node<T> nextNode = node.next;
295      final Node<T> previousNode = node.previous;
296
297      if (nextNode != null)
298      {
299        nextNode.previous = previousNode;
300      }
301
302      if (previousNode != null)
303      {
304        previousNode.next = nextNode;
305      }
306
307      // Fix parent, if it exists.
308      final Node<T> parentNode = node.parent;
309      if (parentNode == null)
310      {
311        // Reached the top of the DIT, so no parents to fix.
312        break;
313      }
314
315      if (parentNode.child != node)
316      {
317        // The parent references another sibling and does not need fixing.
318        break;
319      }
320
321      if (nextNode != null)
322      {
323        // Update child pointer and return.
324        parentNode.child = nextNode;
325        break;
326      }
327
328      if (parentNode.element != null)
329      {
330        // Parent node is still needed as it contains data.
331        break;
332      }
333
334      // The parent node is glue so remove it.
335      ditCacheMap.remove(parentNode.dn);
336
337      // Smash pointers.
338      node.parent = null;
339      node.previous = null;
340      node.next = null;
341
342      // Continue up tree.
343      node = parentNode;
344    }
345  }
346
347
348
349  /**
350   * Returns {@code true} if there are stored objects subordinate to subtree DN.
351   * @param key subtree DN.
352   * @return {@code true} if there are stored objects subordinate to subtree DN.
353   */
354  public boolean containsSubtree(DN key)
355  {
356    return ditCacheMap.containsKey(key);
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      @Override
668      public boolean hasNext()
669      {
670        if (rootNode != null)
671        {
672          if (node == rootNode && rootNode.element != null)
673          {
674            return true;
675          }
676          while (node != null)
677          {
678            if (node.element != null)
679            {
680              return true;
681            }
682            if (node.child != null)
683            {
684              node = node.child;
685            }
686            else
687            {
688              while (node.next == null && node.parent != rootNode)
689              {
690                node = node.parent;
691              }
692              node = node.next;
693            }
694          }
695        }
696        return false;
697      }
698
699      @Override
700      public T next()
701      {
702        T element = null;
703
704        if (rootNode != null)
705        {
706          if (node == rootNode)
707          {
708            node = rootNode.child;
709            if (rootNode.element != null)
710            {
711              return rootNode.element;
712            }
713          }
714          while (node != null)
715          {
716            element = node.element;
717            if (node.child != null)
718            {
719              node = node.child;
720            }
721            else
722            {
723              while (node.next == null && node.parent != rootNode)
724              {
725                node = node.parent;
726              }
727              node = node.next;
728            }
729            if (element != null)
730            {
731              return element;
732            }
733          }
734        }
735        throw new NoSuchElementException();
736      }
737
738      @Override
739      public void remove()
740      {
741        throw new UnsupportedOperationException();
742      }
743    }
744
745    @Override
746    public Iterator<T> iterator()
747    {
748      return new SubtreeSetIterator();
749    }
750
751    @Override
752    public boolean isEmpty()
753    {
754      return !(new SubtreeSetIterator(this.key).hasNext());
755    }
756
757    @Override
758    public int size()
759    {
760      int size = 0;
761
762      Iterator<T> iterator = new SubtreeSetIterator(this.key);
763      while (iterator.hasNext())
764      {
765        iterator.next();
766        size++;
767      }
768
769      return size;
770    }
771  }
772
773  /**
774   * Returns the size of the internal map. Used for testing purposes only.
775   *
776   * @return The size of the internal map.
777   */
778  int getMapSize()
779  {
780    return ditCacheMap.size();
781  }
782}