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.HashMap;
021
022 import 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 */
036 public 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 }