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