1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.hdfs.util;
19 
20 import java.util.Arrays;
21 import java.util.HashMap;
22 
23 import com.google.common.base.Preconditions;
24 import org.apache.commons.lang.ArrayUtils;
25 
26 /**
27  * Counters for an enum type.
28  *
29  * For example, suppose there is an enum type
30  * <pre>
31  * enum Fruit { APPLE, ORANGE, GRAPE }
32  * </pre>
33  * An {@link EnumCounters} object can be created for counting the numbers of
34  * APPLE, ORANGLE and GRAPE.
35  *
36  * @param <E> the enum type
37  */
38 public class EnumCounters<E extends Enum<E>> {
39   /** The class of the enum. */
40   private final Class<E> enumClass;
41   /** An array of longs corresponding to the enum type. */
42   private final long[] counters;
43 
44   /**
45    * Construct counters for the given enum constants.
46    * @param enumClass the enum class of the counters.
47    */
EnumCounters(final Class<E> enumClass)48   public EnumCounters(final Class<E> enumClass) {
49     final E[] enumConstants = enumClass.getEnumConstants();
50     Preconditions.checkNotNull(enumConstants);
51     this.enumClass = enumClass;
52     this.counters = new long[enumConstants.length];
53   }
54 
EnumCounters(final Class<E> enumClass, long defaultVal)55   public EnumCounters(final Class<E> enumClass, long defaultVal) {
56     final E[] enumConstants = enumClass.getEnumConstants();
57     Preconditions.checkNotNull(enumConstants);
58     this.enumClass = enumClass;
59     this.counters = new long[enumConstants.length];
60     reset(defaultVal);
61   }
62 
63   /** @return the value of counter e. */
get(final E e)64   public final long get(final E e) {
65     return counters[e.ordinal()];
66   }
67 
68   /** @return the values of counter as a shadow copy of array*/
asArray()69   public long[] asArray() {
70     return ArrayUtils.clone(counters);
71   }
72 
73   /** Negate all counters. */
negation()74   public final void negation() {
75     for(int i = 0; i < counters.length; i++) {
76       counters[i] = -counters[i];
77     }
78   }
79 
80   /** Set counter e to the given value. */
set(final E e, final long value)81   public final void set(final E e, final long value) {
82     counters[e.ordinal()] = value;
83   }
84 
85   /** Set this counters to that counters. */
set(final EnumCounters<E> that)86   public final void set(final EnumCounters<E> that) {
87     for(int i = 0; i < counters.length; i++) {
88       this.counters[i] = that.counters[i];
89     }
90   }
91 
92   /** Reset all counters to zero. */
reset()93   public final void reset() {
94     reset(0L);
95   }
96 
97   /** Add the given value to counter e. */
add(final E e, final long value)98   public final void add(final E e, final long value) {
99     counters[e.ordinal()] += value;
100   }
101 
102   /** Add that counters to this counters. */
add(final EnumCounters<E> that)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. */
subtract(final E e, final long value)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. */
subtract(final EnumCounters<E> that)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. */
sum()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
equals(Object obj)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
hashCode()143   public int hashCode() {
144     return Arrays.hashCode(counters);
145   }
146 
147   @Override
toString()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 
reset(long val)158   public final void reset(long val) {
159     for(int i = 0; i < counters.length; i++) {
160       this.counters[i] = val;
161     }
162   }
163 
allLessOrEqual(long val)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 
anyGreaterOrEqual(long val)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. */
newInstance()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. */
Map(final Factory<E, C> factory)209     public Map(final Factory<E, C> factory) {
210       this.factory = factory;
211     }
212 
213     /** @return the counters for the given key. */
getCounts(final K 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. */
sum()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. */
sum(final E 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
toString()242     public String toString() {
243       return counts.toString();
244     }
245   }
246 }
247