ObjectIdSubclassMap.java

  1. /*
  2.  * Copyright (C) 2009, Google Inc.
  3.  * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  4.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  5.  * and other copyright owners as documented in the project's IP log.
  6.  *
  7.  * This program and the accompanying materials are made available
  8.  * under the terms of the Eclipse Distribution License v1.0 which
  9.  * accompanies this distribution, is reproduced below, and is
  10.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  11.  *
  12.  * All rights reserved.
  13.  *
  14.  * Redistribution and use in source and binary forms, with or
  15.  * without modification, are permitted provided that the following
  16.  * conditions are met:
  17.  *
  18.  * - Redistributions of source code must retain the above copyright
  19.  *   notice, this list of conditions and the following disclaimer.
  20.  *
  21.  * - Redistributions in binary form must reproduce the above
  22.  *   copyright notice, this list of conditions and the following
  23.  *   disclaimer in the documentation and/or other materials provided
  24.  *   with the distribution.
  25.  *
  26.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  27.  *   names of its contributors may be used to endorse or promote
  28.  *   products derived from this software without specific prior
  29.  *   written permission.
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  34.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  36.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  41.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  42.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  43.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44.  */

  45. package org.eclipse.jgit.lib;

  46. import java.util.Iterator;
  47. import java.util.NoSuchElementException;

  48. /**
  49.  * Fast, efficient map specifically for {@link org.eclipse.jgit.lib.ObjectId}
  50.  * subclasses.
  51.  * <p>
  52.  * This map provides an efficient translation from any ObjectId instance to a
  53.  * cached subclass of ObjectId that has the same value.
  54.  * <p>
  55.  * If object instances are stored in only one map,
  56.  * {@link org.eclipse.jgit.lib.ObjectIdOwnerMap} is a more efficient
  57.  * implementation.
  58.  *
  59.  * @param <V>
  60.  *            type of subclass of ObjectId that will be stored in the map.
  61.  */
  62. public class ObjectIdSubclassMap<V extends ObjectId>
  63.         implements Iterable<V>, ObjectIdSet {
  64.     private static final int INITIAL_TABLE_SIZE = 2048;

  65.     int size;

  66.     private int grow;

  67.     private int mask;

  68.     V[] table;

  69.     /**
  70.      * Create an empty map.
  71.      */
  72.     public ObjectIdSubclassMap() {
  73.         initTable(INITIAL_TABLE_SIZE);
  74.     }

  75.     /**
  76.      * Remove all entries from this map.
  77.      */
  78.     public void clear() {
  79.         size = 0;
  80.         initTable(INITIAL_TABLE_SIZE);
  81.     }

  82.     /**
  83.      * Lookup an existing mapping.
  84.      *
  85.      * @param toFind
  86.      *            the object identifier to find.
  87.      * @return the instance mapped to toFind, or null if no mapping exists.
  88.      */
  89.     public V get(AnyObjectId toFind) {
  90.         final int msk = mask;
  91.         int i = toFind.w1 & msk;
  92.         final V[] tbl = table;
  93.         V obj;

  94.         while ((obj = tbl[i]) != null) {
  95.             if (AnyObjectId.isEqual(obj, toFind)) {
  96.                 return obj;
  97.             }
  98.             i = (i + 1) & msk;
  99.         }
  100.         return null;
  101.     }

  102.     /**
  103.      * {@inheritDoc}
  104.      * <p>
  105.      * Returns true if this map contains the specified object.
  106.      */
  107.     @Override
  108.     public boolean contains(AnyObjectId toFind) {
  109.         return get(toFind) != null;
  110.     }

  111.     /**
  112.      * Store an object for future lookup.
  113.      * <p>
  114.      * An existing mapping for <b>must not</b> be in this map. Callers must
  115.      * first call {@link #get(AnyObjectId)} to verify there is no current
  116.      * mapping prior to adding a new mapping, or use
  117.      * {@link #addIfAbsent(ObjectId)}.
  118.      *
  119.      * @param newValue
  120.      *            the object to store.
  121.      */
  122.     public <Q extends V> void add(Q newValue) {
  123.         if (++size == grow)
  124.             grow();
  125.         insert(newValue);
  126.     }

  127.     /**
  128.      * Store an object for future lookup.
  129.      * <p>
  130.      * Stores {@code newValue}, but only if there is not already an object for
  131.      * the same object name. Callers can tell if the value is new by checking
  132.      * the return value with reference equality:
  133.      *
  134.      * <pre>
  135.      * V obj = ...;
  136.      * boolean wasNew = map.addIfAbsent(obj) == obj;
  137.      * </pre>
  138.      *
  139.      * @param newValue
  140.      *            the object to store.
  141.      * @return {@code newValue} if stored, or the prior value already stored and
  142.      *         that would have been returned had the caller used
  143.      *         {@code get(newValue)} first.
  144.      */
  145.     public <Q extends V> V addIfAbsent(Q newValue) {
  146.         final int msk = mask;
  147.         int i = newValue.w1 & msk;
  148.         final V[] tbl = table;
  149.         V obj;

  150.         while ((obj = tbl[i]) != null) {
  151.             if (AnyObjectId.isEqual(obj, newValue))
  152.                 return obj;
  153.             i = (i + 1) & msk;
  154.         }

  155.         if (++size == grow) {
  156.             grow();
  157.             insert(newValue);
  158.         } else {
  159.             tbl[i] = newValue;
  160.         }
  161.         return newValue;
  162.     }

  163.     /**
  164.      * Get number of objects in map
  165.      *
  166.      * @return number of objects in map
  167.      */
  168.     public int size() {
  169.         return size;
  170.     }

  171.     /**
  172.      * Whether {@link #size()} is 0.
  173.      *
  174.      * @return true if {@link #size()} is 0.
  175.      */
  176.     public boolean isEmpty() {
  177.         return size == 0;
  178.     }

  179.     /** {@inheritDoc} */
  180.     @Override
  181.     public Iterator<V> iterator() {
  182.         return new Iterator<V>() {
  183.             private int found;

  184.             private int i;

  185.             @Override
  186.             public boolean hasNext() {
  187.                 return found < size;
  188.             }

  189.             @Override
  190.             public V next() {
  191.                 while (i < table.length) {
  192.                     final V v = table[i++];
  193.                     if (v != null) {
  194.                         found++;
  195.                         return v;
  196.                     }
  197.                 }
  198.                 throw new NoSuchElementException();
  199.             }

  200.             @Override
  201.             public void remove() {
  202.                 throw new UnsupportedOperationException();
  203.             }
  204.         };
  205.     }

  206.     private void insert(V newValue) {
  207.         final int msk = mask;
  208.         int j = newValue.w1 & msk;
  209.         final V[] tbl = table;
  210.         while (tbl[j] != null)
  211.             j = (j + 1) & msk;
  212.         tbl[j] = newValue;
  213.     }

  214.     private void grow() {
  215.         final V[] oldTable = table;
  216.         final int oldSize = table.length;

  217.         initTable(oldSize << 1);
  218.         for (int i = 0; i < oldSize; i++) {
  219.             final V obj = oldTable[i];
  220.             if (obj != null)
  221.                 insert(obj);
  222.         }
  223.     }

  224.     private void initTable(int sz) {
  225.         grow = sz >> 1;
  226.         mask = sz - 1;
  227.         table = createArray(sz);
  228.     }

  229.     @SuppressWarnings("unchecked")
  230.     private final V[] createArray(int sz) {
  231.         return (V[]) new ObjectId[sz];
  232.     }
  233. }