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.HashMap; 021 022 import com.google.common.base.Preconditions; 023 024 /** 025 * Counters for an enum type. 026 * 027 * For example, suppose there is an enum type 028 * <pre> 029 * enum Fruit { APPLE, ORANGE, GRAPE } 030 * </pre> 031 * An {@link EnumCounters} object can be created for counting the numbers of 032 * APPLE, ORANGLE and GRAPE. 033 * 034 * @param <E> the enum type 035 */ 036 public class EnumCounters<E extends Enum<E>> { 037 /** An array of enum constants. */ 038 private final E[] enumConstants; 039 /** The counter array, counters[i] corresponds to the enumConstants[i]. */ 040 private final long[] counters; 041 042 /** 043 * Construct counters for the given enum constants. 044 * @param enumConstants an array of enum constants such that, 045 * for all i, enumConstants[i].ordinal() == i. 046 */ 047 public EnumCounters(final E[] enumConstants) { 048 for(int i = 0; i < enumConstants.length; i++) { 049 Preconditions.checkArgument(enumConstants[i].ordinal() == i); 050 } 051 this.enumConstants = enumConstants; 052 this.counters = new long[enumConstants.length]; 053 } 054 055 /** @return the value of counter e. */ 056 public final long get(final E e) { 057 return counters[e.ordinal()]; 058 } 059 060 /** Negate all counters. */ 061 public final void negation() { 062 for(int i = 0; i < counters.length; i++) { 063 counters[i] = -counters[i]; 064 } 065 } 066 067 /** Set counter e to the given value. */ 068 public final void set(final E e, final long value) { 069 counters[e.ordinal()] = value; 070 } 071 072 /** Add the given value to counter e. */ 073 public final void add(final E e, final long value) { 074 counters[e.ordinal()] += value; 075 } 076 077 /** Add that counters to this counters. */ 078 public final void add(final EnumCounters<E> that) { 079 for(int i = 0; i < counters.length; i++) { 080 this.counters[i] += that.counters[i]; 081 } 082 } 083 084 /** Subtract the given value from counter e. */ 085 public final void subtract(final E e, final long value) { 086 counters[e.ordinal()] -= value; 087 } 088 089 /** Subtract that counters from this counters. */ 090 public final void subtract(final EnumCounters<E> that) { 091 for(int i = 0; i < counters.length; i++) { 092 this.counters[i] -= that.counters[i]; 093 } 094 } 095 096 @Override 097 public String toString() { 098 final StringBuilder b = new StringBuilder(); 099 for(int i = 0; i < counters.length; i++) { 100 final String name = enumConstants[i].name(); 101 b.append(name).append("=").append(counters[i]).append(", "); 102 } 103 return b.substring(0, b.length() - 2); 104 } 105 106 /** 107 * A factory for creating counters. 108 * 109 * @param <E> the enum type 110 * @param <C> the counter type 111 */ 112 public static interface Factory<E extends Enum<E>, 113 C extends EnumCounters<E>> { 114 /** Create a new counters instance. */ 115 public C newInstance(); 116 } 117 118 /** 119 * A key-value map which maps the keys to {@link EnumCounters}. 120 * Note that null key is supported. 121 * 122 * @param <K> the key type 123 * @param <E> the enum type 124 * @param <C> the counter type 125 */ 126 public static class Map<K, E extends Enum<E>, C extends EnumCounters<E>> { 127 /** The factory for creating counters. */ 128 private final Factory<E, C> factory; 129 /** Key-to-Counts map. */ 130 private final java.util.Map<K, C> counts = new HashMap<K, C>(); 131 132 /** Construct a map. */ 133 public Map(final Factory<E, C> factory) { 134 this.factory = factory; 135 } 136 137 /** @return the counters for the given key. */ 138 public final C getCounts(final K key) { 139 C c = counts.get(key); 140 if (c == null) { 141 c = factory.newInstance(); 142 counts.put(key, c); 143 } 144 return c; 145 } 146 147 /** @return the sum of the values of all the counters. */ 148 public final C sum() { 149 final C sum = factory.newInstance(); 150 for(C c : counts.values()) { 151 sum.add(c); 152 } 153 return sum; 154 } 155 156 /** @return the sum of the values of all the counters for e. */ 157 public final long sum(final E e) { 158 long sum = 0; 159 for(C c : counts.values()) { 160 sum += c.get(e); 161 } 162 return sum; 163 } 164 165 @Override 166 public String toString() { 167 return counts.toString(); 168 } 169 } 170 }