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 }