001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hdfs.util;
019
020import java.util.HashMap;
021
022import com.google.common.base.Preconditions;
023
024/**
025 * Counters for an enum type.
026 * 
027 * For example, suppose there is an enum type
028 * <pre>
029 * enum Fruit { APPLE, ORANGE, GRAPE }
030 * </pre>
031 * An {@link EnumCounters} object can be created for counting the numbers of
032 * APPLE, ORANGLE and GRAPE.
033 *
034 * @param <E> the enum type
035 */
036public class EnumCounters<E extends Enum<E>> {
037  /** An array of enum constants. */
038  private final E[] enumConstants;
039  /** The counter array, counters[i] corresponds to the enumConstants[i]. */
040  private final long[] counters;
041
042  /**
043   * Construct counters for the given enum constants.
044   * @param enumConstants an array of enum constants such that, 
045   *                      for all i, enumConstants[i].ordinal() == i.
046   */
047  public EnumCounters(final E[] enumConstants) {
048    for(int i = 0; i < enumConstants.length; i++) {
049      Preconditions.checkArgument(enumConstants[i].ordinal() == i);
050    }
051    this.enumConstants = enumConstants;
052    this.counters = new long[enumConstants.length];
053  }
054  
055  /** @return the value of counter e. */
056  public final long get(final E e) {
057    return counters[e.ordinal()];
058  }
059
060  /** Negate all counters. */
061  public final void negation() {
062    for(int i = 0; i < counters.length; i++) {
063      counters[i] = -counters[i];
064    }
065  }
066  
067  /** Set counter e to the given value. */
068  public final void set(final E e, final long value) {
069    counters[e.ordinal()] = value;
070  }
071
072  /** Add the given value to counter e. */
073  public final void add(final E e, final long value) {
074    counters[e.ordinal()] += value;
075  }
076
077  /** Add that counters to this counters. */
078  public final void add(final EnumCounters<E> that) {
079    for(int i = 0; i < counters.length; i++) {
080      this.counters[i] += that.counters[i];
081    }
082  }
083
084  /** Subtract the given value from counter e. */
085  public final void subtract(final E e, final long value) {
086    counters[e.ordinal()] -= value;
087  }
088
089  /** Subtract that counters from this counters. */
090  public final void subtract(final EnumCounters<E> that) {
091    for(int i = 0; i < counters.length; i++) {
092      this.counters[i] -= that.counters[i];
093    }
094  }
095
096  @Override
097  public String toString() {
098    final StringBuilder b = new StringBuilder();
099    for(int i = 0; i < counters.length; i++) {
100      final String name = enumConstants[i].name();
101      b.append(name).append("=").append(counters[i]).append(", ");
102    }
103    return b.substring(0, b.length() - 2);
104  }
105
106  /**
107   * A factory for creating counters.
108   * 
109   * @param <E> the enum type
110   * @param <C> the counter type
111   */
112  public static interface Factory<E extends Enum<E>,
113                                  C extends EnumCounters<E>> {
114    /** Create a new counters instance. */
115    public C newInstance(); 
116  }
117
118  /**
119   * A key-value map which maps the keys to {@link EnumCounters}.
120   * Note that null key is supported.
121   *
122   * @param <K> the key type
123   * @param <E> the enum type
124   * @param <C> the counter type
125   */
126  public static class Map<K, E extends Enum<E>, C extends EnumCounters<E>> {
127    /** The factory for creating counters. */
128    private final Factory<E, C> factory;
129    /** Key-to-Counts map. */
130    private final java.util.Map<K, C> counts = new HashMap<K, C>();
131    
132    /** Construct a map. */
133    public Map(final Factory<E, C> factory) {
134      this.factory = factory;
135    }
136
137    /** @return the counters for the given key. */
138    public final C getCounts(final K key) {
139      C c = counts.get(key);
140      if (c == null) {
141        c = factory.newInstance();
142        counts.put(key, c); 
143      }
144      return c;
145    }
146    
147    /** @return the sum of the values of all the counters. */
148    public final C sum() {
149      final C sum = factory.newInstance();
150      for(C c : counts.values()) {
151        sum.add(c);
152      }
153      return sum;
154    }
155    
156    /** @return the sum of the values of all the counters for e. */
157    public final long sum(final E e) {
158      long sum = 0;
159      for(C c : counts.values()) {
160        sum += c.get(e);
161      }
162      return sum;
163    }
164
165    @Override
166    public String toString() {
167      return counts.toString();
168    }
169  }
170}