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 /** The counter array, counters[i] corresponds to the enumConstants[i]. */
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 /** Add the given value to counter e. */
079 public final void add(final E e, final long value) {
080 counters[e.ordinal()] += value;
081 }
082
083 /** Add that counters to this counters. */
084 public final void add(final EnumCounters<E> that) {
085 for(int i = 0; i < counters.length; i++) {
086 this.counters[i] += that.counters[i];
087 }
088 }
089
090 /** Subtract the given value from counter e. */
091 public final void subtract(final E e, final long value) {
092 counters[e.ordinal()] -= value;
093 }
094
095 /** Subtract this counters from that counters. */
096 public final void subtract(final EnumCounters<E> that) {
097 for(int i = 0; i < counters.length; i++) {
098 this.counters[i] -= that.counters[i];
099 }
100 }
101
102 @Override
103 public boolean equals(Object obj) {
104 if (obj == this) {
105 return true;
106 } else if (obj == null || !(obj instanceof EnumCounters)) {
107 return false;
108 }
109 final EnumCounters<?> that = (EnumCounters<?>)obj;
110 return this.enumClass == that.enumClass
111 && Arrays.equals(this.counters, that.counters);
112 }
113
114 @Override
115 public int hashCode() {
116 return Arrays.hashCode(counters);
117 }
118
119 @Override
120 public String toString() {
121 final E[] enumConstants = enumClass.getEnumConstants();
122 final StringBuilder b = new StringBuilder();
123 for(int i = 0; i < counters.length; i++) {
124 final String name = enumConstants[i].name();
125 b.append(name).append("=").append(counters[i]).append(", ");
126 }
127 return b.substring(0, b.length() - 2);
128 }
129
130 /**
131 * A factory for creating counters.
132 *
133 * @param <E> the enum type
134 * @param <C> the counter type
135 */
136 public static interface Factory<E extends Enum<E>,
137 C extends EnumCounters<E>> {
138 /** Create a new counters instance. */
139 public C newInstance();
140 }
141
142 /**
143 * A key-value map which maps the keys to {@link EnumCounters}.
144 * Note that null key is supported.
145 *
146 * @param <K> the key type
147 * @param <E> the enum type
148 * @param <C> the counter type
149 */
150 public static class Map<K, E extends Enum<E>, C extends EnumCounters<E>> {
151 /** The factory for creating counters. */
152 private final Factory<E, C> factory;
153 /** Key-to-Counts map. */
154 private final java.util.Map<K, C> counts = new HashMap<K, C>();
155
156 /** Construct a map. */
157 public Map(final Factory<E, C> factory) {
158 this.factory = factory;
159 }
160
161 /** @return the counters for the given key. */
162 public final C getCounts(final K key) {
163 C c = counts.get(key);
164 if (c == null) {
165 c = factory.newInstance();
166 counts.put(key, c);
167 }
168 return c;
169 }
170
171 /** @return the sum of the values of all the counters. */
172 public final C sum() {
173 final C sum = factory.newInstance();
174 for(C c : counts.values()) {
175 sum.add(c);
176 }
177 return sum;
178 }
179
180 /** @return the sum of the values of all the counters for e. */
181 public final long sum(final E e) {
182 long sum = 0;
183 for(C c : counts.values()) {
184 sum += c.get(e);
185 }
186 return sum;
187 }
188
189 @Override
190 public String toString() {
191 return counts.toString();
192 }
193 }
194 }