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    }