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