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 }