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.Arrays;
021import java.util.HashMap;
022
023import com.google.common.base.Preconditions;
024import org.apache.commons.lang.ArrayUtils;
025
026/**
027 * Counters for an enum type.
028 * 
029 * For example, suppose there is an enum type
030 * <pre>
031 * enum Fruit { APPLE, ORANGE, GRAPE }
032 * </pre>
033 * An {@link EnumCounters} object can be created for counting the numbers of
034 * APPLE, ORANGLE and GRAPE.
035 *
036 * @param <E> the enum type
037 */
038public class EnumCounters<E extends Enum<E>> {
039  /** The class of the enum. */
040  private final Class<E> enumClass;
041  /** An array of longs corresponding to the enum type. */
042  private final long[] counters;
043
044  /**
045   * Construct counters for the given enum constants.
046   * @param enumClass the enum class of the counters.
047   */
048  public EnumCounters(final Class<E> enumClass) {
049    final E[] enumConstants = enumClass.getEnumConstants();
050    Preconditions.checkNotNull(enumConstants);
051    this.enumClass = enumClass;
052    this.counters = new long[enumConstants.length];
053  }
054
055  public EnumCounters(final Class<E> enumClass, long defaultVal) {
056    final E[] enumConstants = enumClass.getEnumConstants();
057    Preconditions.checkNotNull(enumConstants);
058    this.enumClass = enumClass;
059    this.counters = new long[enumConstants.length];
060    reset(defaultVal);
061  }
062  
063  /** @return the value of counter e. */
064  public final long get(final E e) {
065    return counters[e.ordinal()];
066  }
067
068  /** @return the values of counter as a shadow copy of array*/
069  public long[] asArray() {
070    return ArrayUtils.clone(counters);
071  }
072
073  /** Negate all counters. */
074  public final void negation() {
075    for(int i = 0; i < counters.length; i++) {
076      counters[i] = -counters[i];
077    }
078  }
079  
080  /** Set counter e to the given value. */
081  public final void set(final E e, final long value) {
082    counters[e.ordinal()] = value;
083  }
084
085  /** Set this counters to that counters. */
086  public final void set(final EnumCounters<E> that) {
087    for(int i = 0; i < counters.length; i++) {
088      this.counters[i] = that.counters[i];
089    }
090  }
091
092  /** Reset all counters to zero. */
093  public final void reset() {
094    reset(0L);
095  }
096
097  /** Add the given value to counter e. */
098  public final void add(final E e, final long value) {
099    counters[e.ordinal()] += value;
100  }
101
102  /** Add that counters to this counters. */
103  public final void add(final EnumCounters<E> that) {
104    for(int i = 0; i < counters.length; i++) {
105      this.counters[i] += that.counters[i];
106    }
107  }
108
109  /** Subtract the given value from counter e. */
110  public final void subtract(final E e, final long value) {
111    counters[e.ordinal()] -= value;
112  }
113
114  /** Subtract this counters from that counters. */
115  public final void subtract(final EnumCounters<E> that) {
116    for(int i = 0; i < counters.length; i++) {
117      this.counters[i] -= that.counters[i];
118    }
119  }
120  
121  /** @return the sum of all counters. */
122  public final long sum() {
123    long sum = 0;
124    for(int i = 0; i < counters.length; i++) {
125      sum += counters[i];
126    }
127    return sum;
128  }
129
130  @Override
131  public boolean equals(Object obj) {
132    if (obj == this) {
133      return true;
134    } else if (obj == null || !(obj instanceof EnumCounters)) {
135      return false;
136    }
137    final EnumCounters<?> that = (EnumCounters<?>)obj;
138    return this.enumClass == that.enumClass
139        && Arrays.equals(this.counters, that.counters);
140  }
141
142  @Override
143  public int hashCode() {
144    return Arrays.hashCode(counters);
145  }
146
147  @Override
148  public String toString() {
149    final E[] enumConstants = enumClass.getEnumConstants();
150    final StringBuilder b = new StringBuilder();
151    for(int i = 0; i < counters.length; i++) {
152      final String name = enumConstants[i].name();
153      b.append(name).append("=").append(counters[i]).append(", ");
154    }
155    return b.substring(0, b.length() - 2);
156  }
157
158  public final void reset(long val) {
159    for(int i = 0; i < counters.length; i++) {
160      this.counters[i] = val;
161    }
162  }
163
164  public boolean allLessOrEqual(long val) {
165    for (long c : counters) {
166      if (c > val) {
167        return false;
168      }
169    }
170    return true;
171  }
172
173  public boolean anyGreaterOrEqual(long val) {
174    for (long c: counters) {
175      if (c >= val) {
176        return true;
177      }
178    }
179    return false;
180  }
181
182  /**
183   * A factory for creating counters.
184   * 
185   * @param <E> the enum type
186   * @param <C> the counter type
187   */
188  public static interface Factory<E extends Enum<E>,
189                                  C extends EnumCounters<E>> {
190    /** Create a new counters instance. */
191    public C newInstance(); 
192  }
193
194  /**
195   * A key-value map which maps the keys to {@link EnumCounters}.
196   * Note that null key is supported.
197   *
198   * @param <K> the key type
199   * @param <E> the enum type
200   * @param <C> the counter type
201   */
202  public static class Map<K, E extends Enum<E>, C extends EnumCounters<E>> {
203    /** The factory for creating counters. */
204    private final Factory<E, C> factory;
205    /** Key-to-Counts map. */
206    private final java.util.Map<K, C> counts = new HashMap<K, C>();
207    
208    /** Construct a map. */
209    public Map(final Factory<E, C> factory) {
210      this.factory = factory;
211    }
212
213    /** @return the counters for the given key. */
214    public final C getCounts(final K key) {
215      C c = counts.get(key);
216      if (c == null) {
217        c = factory.newInstance();
218        counts.put(key, c); 
219      }
220      return c;
221    }
222    
223    /** @return the sum of the values of all the counters. */
224    public final C sum() {
225      final C sum = factory.newInstance();
226      for(C c : counts.values()) {
227        sum.add(c);
228      }
229      return sum;
230    }
231    
232    /** @return the sum of the values of all the counters for e. */
233    public final long sum(final E e) {
234      long sum = 0;
235      for(C c : counts.values()) {
236        sum += c.get(e);
237      }
238      return sum;
239    }
240
241    @Override
242    public String toString() {
243      return counts.toString();
244    }
245  }
246}