001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.connector.federation.contribution;
025
026 import java.util.Collections;
027 import java.util.HashMap;
028 import java.util.LinkedList;
029 import java.util.List;
030 import java.util.Map;
031 import java.util.concurrent.ConcurrentHashMap;
032 import java.util.concurrent.ConcurrentMap;
033 import java.util.concurrent.atomic.AtomicLong;
034 import org.jboss.dna.common.util.CheckArg;
035 import org.jboss.dna.common.util.HashCode;
036
037 /**
038 * Simple utility class to record the distribution of contributions.
039 *
040 * @author Randall Hauch
041 */
042 public class ContributionStatistics {
043
044 /**
045 * This should only be enabled when attempting to accumulate the distribution, and should <i>never</i> be enabled in a
046 * release.
047 */
048 /*package*/static final boolean RECORD = false;
049
050 private static final ConcurrentMap<Stats, AtomicLong> DATA = new ConcurrentHashMap<Stats, AtomicLong>();
051
052 /**
053 * Record a {@link Contribution} was created for the supplied number of properties and children. {@link #RECORD} should be
054 * checked <i>before</i> this method is called.
055 *
056 * @param propertyCount the number of properties
057 * @param childrenCount the number of children
058 */
059 /*package*/static void record( int propertyCount,
060 int childrenCount ) {
061 Stats key = new Stats(propertyCount, childrenCount);
062 AtomicLong existing = DATA.putIfAbsent(key, new AtomicLong(1l));
063 if (existing != null) existing.incrementAndGet();
064 }
065
066 public boolean isRecording() {
067 return RECORD;
068 }
069
070 /**
071 * Get a copy of the raw statistics data.
072 *
073 * @return a copy of the data; never null
074 */
075 public static Map<Stats, AtomicLong> getData() {
076 return new HashMap<Stats, AtomicLong>(DATA);
077 }
078
079 /**
080 * Get the N most populous combinations of properties and children counts.
081 *
082 * @param n the maximum number of data objects to find and return in the raw data; must be positive
083 * @return the list of N (or fewer)
084 */
085 public static List<Data> getTop( int n ) {
086 CheckArg.isPositive(n, "n");
087 LinkedList<Data> results = new LinkedList<Data>();
088 for (Map.Entry<Stats, AtomicLong> entry : DATA.entrySet()) {
089 long value = entry.getValue().get();
090 if (results.size() >= n) {
091 Data last = results.getLast();
092 if (value <= last.getInstanceCount()) continue;
093 // The new count is larger than the smallest, so pop the smallest and add the newest ...
094 results.removeLast();
095 }
096 results.add(new Data(entry.getKey(), value));
097 Collections.sort(results);
098 }
099 return results;
100 }
101
102 public static final class Data implements Comparable<Data> {
103 private final int propertyCount;
104 private final int childrenCount;
105 private final long instanceCount;
106
107 protected Data( Stats stats,
108 long instanceCount ) {
109 this.propertyCount = stats.getPropertyCount();
110 this.childrenCount = stats.getChildrenCount();
111 this.instanceCount = instanceCount;
112 }
113
114 /**
115 * @return childrenCount
116 */
117 public int getChildrenCount() {
118 return childrenCount;
119 }
120
121 /**
122 * @return propertyCount
123 */
124 public int getPropertyCount() {
125 return propertyCount;
126 }
127
128 /**
129 * @return instanceCount
130 */
131 public long getInstanceCount() {
132 return instanceCount;
133 }
134
135 /**
136 * {@inheritDoc} This orders the values in the opposite order, so that those with larger numbers of instances appear
137 * first.
138 *
139 * @see java.lang.Comparable#compareTo(java.lang.Object)
140 */
141 public int compareTo( Data that ) {
142 long diff = that.getInstanceCount() - this.getInstanceCount(); // backwards
143 return diff < 0l ? -1 : diff > 0 ? 1 : 0;
144 }
145
146 /**
147 * {@inheritDoc}
148 *
149 * @see java.lang.Object#hashCode()
150 */
151 @Override
152 public int hashCode() {
153 return HashCode.compute(this.propertyCount, this.childrenCount, this.instanceCount);
154 }
155
156 /**
157 * {@inheritDoc}
158 *
159 * @see java.lang.Object#equals(java.lang.Object)
160 */
161 @Override
162 public boolean equals( Object obj ) {
163 if (obj instanceof Data) {
164 Data that = (Data)obj;
165 if (this.propertyCount != that.propertyCount) return false;
166 if (this.childrenCount != that.childrenCount) return false;
167 if (this.instanceCount != that.instanceCount) return false;
168 return true;
169 }
170 return false;
171 }
172
173 /**
174 * {@inheritDoc}
175 *
176 * @see java.lang.Object#toString()
177 */
178 @Override
179 public String toString() {
180 return "(# props=" + this.getPropertyCount() + ", # children=" + this.getChildrenCount() + ") => "
181 + this.getInstanceCount();
182 }
183 }
184
185 public static final class Stats {
186 private final int propertyCount;
187 private final int childrenCount;
188
189 protected Stats( int propertyCount,
190 int childrenCount ) {
191 this.propertyCount = propertyCount;
192 this.childrenCount = childrenCount;
193 }
194
195 /**
196 * @return childrenCount
197 */
198 public int getChildrenCount() {
199 return childrenCount;
200 }
201
202 /**
203 * @return propertyCount
204 */
205 public int getPropertyCount() {
206 return propertyCount;
207 }
208
209 /**
210 * {@inheritDoc}
211 *
212 * @see java.lang.Object#hashCode()
213 */
214 @Override
215 public int hashCode() {
216 return HashCode.compute(this.propertyCount, this.childrenCount);
217 }
218
219 /**
220 * {@inheritDoc}
221 *
222 * @see java.lang.Object#equals(java.lang.Object)
223 */
224 @Override
225 public boolean equals( Object obj ) {
226 if (obj instanceof Stats) {
227 Stats that = (Stats)obj;
228 if (this.propertyCount != that.propertyCount) return false;
229 if (this.childrenCount != that.childrenCount) return false;
230 return true;
231 }
232 return false;
233 }
234 }
235
236 }