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 }