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 */ 018package org.apache.hadoop.hdfs.util; 019 020import java.util.Arrays; 021import java.util.HashMap; 022 023import com.google.common.base.Preconditions; 024import org.apache.commons.lang.ArrayUtils; 025 026/** 027 * Counters for an enum type. 028 * 029 * For example, suppose there is an enum type 030 * <pre> 031 * enum Fruit { APPLE, ORANGE, GRAPE } 032 * </pre> 033 * An {@link EnumCounters} object can be created for counting the numbers of 034 * APPLE, ORANGLE and GRAPE. 035 * 036 * @param <E> the enum type 037 */ 038public class EnumCounters<E extends Enum<E>> { 039 /** The class of the enum. */ 040 private final Class<E> enumClass; 041 /** An array of longs corresponding to the enum type. */ 042 private final long[] counters; 043 044 /** 045 * Construct counters for the given enum constants. 046 * @param enumClass the enum class of the counters. 047 */ 048 public EnumCounters(final Class<E> enumClass) { 049 final E[] enumConstants = enumClass.getEnumConstants(); 050 Preconditions.checkNotNull(enumConstants); 051 this.enumClass = enumClass; 052 this.counters = new long[enumConstants.length]; 053 } 054 055 public EnumCounters(final Class<E> enumClass, long defaultVal) { 056 final E[] enumConstants = enumClass.getEnumConstants(); 057 Preconditions.checkNotNull(enumConstants); 058 this.enumClass = enumClass; 059 this.counters = new long[enumConstants.length]; 060 reset(defaultVal); 061 } 062 063 /** @return the value of counter e. */ 064 public final long get(final E e) { 065 return counters[e.ordinal()]; 066 } 067 068 /** @return the values of counter as a shadow copy of array*/ 069 public long[] asArray() { 070 return ArrayUtils.clone(counters); 071 } 072 073 /** Negate all counters. */ 074 public final void negation() { 075 for(int i = 0; i < counters.length; i++) { 076 counters[i] = -counters[i]; 077 } 078 } 079 080 /** Set counter e to the given value. */ 081 public final void set(final E e, final long value) { 082 counters[e.ordinal()] = value; 083 } 084 085 /** Set this counters to that counters. */ 086 public final void set(final EnumCounters<E> that) { 087 for(int i = 0; i < counters.length; i++) { 088 this.counters[i] = that.counters[i]; 089 } 090 } 091 092 /** Reset all counters to zero. */ 093 public final void reset() { 094 reset(0L); 095 } 096 097 /** Add the given value to counter e. */ 098 public final void add(final E e, final long value) { 099 counters[e.ordinal()] += value; 100 } 101 102 /** Add that counters to this counters. */ 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. */ 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. */ 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. */ 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 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 143 public int hashCode() { 144 return Arrays.hashCode(counters); 145 } 146 147 @Override 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 158 public final void reset(long val) { 159 for(int i = 0; i < counters.length; i++) { 160 this.counters[i] = val; 161 } 162 } 163 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 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. */ 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. */ 209 public Map(final Factory<E, C> factory) { 210 this.factory = factory; 211 } 212 213 /** @return the counters for the given 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. */ 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. */ 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 242 public String toString() { 243 return counts.toString(); 244 } 245 } 246}