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