001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.monitoring.counters;
019    
020    import java.util.ArrayList;
021    import java.util.Collections;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Set;
025    import java.util.concurrent.ConcurrentHashMap;
026    import java.util.concurrent.CopyOnWriteArraySet;
027    
028    /**
029     * Units allow monitored data to have get typed. A primary unit is the
030     * finest unit for a data type. A primary unit can have derived units,
031     * that represent the same data type, but with a scale factor.
032     * A primary Unit is created with the {@link Unit#Unit(String)} constructor.
033     * A derived Unit is created with the {@link Unit#Unit(String, Unit, long)} constructor.
034     * <p/>
035     * A primary Unit maintains a Map of it's derived units. {@see Unit#getDerived()} can be
036     * used to retrieve the complete list, and {@see Unit#getDerived(String)} to retrieve a
037     * derived unit by it's name.
038     *
039     * @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
040     */
041    public class Unit implements Comparable<Unit> {
042        private static final Map<String, Unit> UNITS = new ConcurrentHashMap<String, Unit>();
043    
044        /**
045         * Time based units
046         */
047        public static class Time extends Unit {
048            public static final Unit NANOSECOND = new Unit("ns");
049            public static final Unit MICROSECOND = new Unit("µs", NANOSECOND, 1000);
050            public static final Unit MILLISECOND = new Unit("ms", MICROSECOND, 1000);
051            public static final Unit SECOND = new Unit("s", MILLISECOND, 1000);
052            public static final Unit MINUTE = new Unit("min", SECOND, 60);
053            public static final Unit HOUR = new Unit("h", MINUTE, 60);
054            public static final Unit DAY = new Unit("day", HOUR, 24);
055    
056            public Time(String name) {
057                super(name);
058            }
059    
060            public Time(String name, Unit derived, long scale) {
061                super(name, derived, scale);
062            }
063    
064        }
065    
066        /**
067         * Binary data units
068         */
069        public static class Binary
070            extends Unit {
071    
072            public static final Unit BYTE = new Unit("b");
073    
074            public static final Unit KBYTE = new Unit("Kb", BYTE, 1024);
075    
076            public static final Unit MBYTE = new Unit("Mb", KBYTE, 1024);
077    
078            public static final Unit GBYTE = new Unit("Gb", MBYTE, 1024);
079    
080            public Binary(String name) {
081                super(name);
082            }
083    
084            public Binary(String name, Unit derived, long scale) {
085                super(name, derived, scale);
086            }
087    
088    
089        }
090    
091        /**
092         * unit for basic item counters & gauges
093         */
094        // "BILLION" does not have same signification depending on country (10^12 or 10^9).
095        // We use International system of unit names to avoid confusion
096        // @see http://en.wikipedia.org/wiki/SI
097        public static final Unit UNARY = new Unit("u");
098        public static final Unit DECA = new Unit("*10", UNARY, 10);
099        public static final Unit HECTO = new Unit("*100", DECA, 10);
100        public static final Unit KILO = new Unit("*1000", HECTO, 10);
101        public static final Unit MEGA = new Unit("*10^6", KILO, 1000);
102        public static final Unit GIGA = new Unit("*10^9", MEGA, 1000);
103        public static final Unit TERA = new Unit("*10^12", GIGA, 1000);
104    
105        private final String name;
106    
107        private final long scale;
108    
109        private Unit primary;
110    
111        private List<Unit> derived;
112    
113        public Set<Unit> primaryUnits = new CopyOnWriteArraySet<Unit>();
114    
115    
116        public static Unit get(String name) {
117            return UNITS.get(name);
118        }
119    
120        /**
121         * Constructor for a primary unit
122         *
123         * @param name
124         */
125        public Unit(String name) {
126            this.name = name;
127            this.primary = this;
128            this.scale = 1;
129            this.derived = new ArrayList<Unit>();
130            this.derived.add(this);
131            primaryUnits.add(this);
132            UNITS.put(name, this);
133        }
134    
135        /**
136         * Constructor for a derived unit
137         *
138         * @param name
139         * @param derived the unit this unit is derived from
140         * @param scale   the scale factor to convert to derived unit
141         */
142        public Unit(String name, Unit derived, long scale) {
143            this.name = name;
144            this.primary = derived.isPrimary() ? derived : derived.getPrimary();
145            this.scale = scale * derived.getScale();
146            primary.derived.add(this);
147            Collections.sort(primary.derived);
148            UNITS.put(name, this);
149        }
150    
151        public Unit getDerived(String name) {
152            for (Unit unit : derived) {
153                if (unit.name.equals(name)) {
154                    return unit;
155                }
156            }
157            return null;
158        }
159    
160        public List<Unit> getDerived() {
161            return Collections.unmodifiableList(derived);
162        }
163    
164    
165        public String getName() {
166            return name;
167        }
168    
169        public long getScale() {
170            return scale;
171        }
172    
173        /**
174         * Convert value from unit to this unit (if conpatible)
175         *
176         * @param value value to convert
177         * @param unit  unit of value
178         * @return value converted to this unit
179         */
180        public double convert(final double value, final Unit unit) {
181            if (unit == this) {
182                return value;
183            }
184            if (!isCompatible(unit)) {
185                throw new IllegalArgumentException("unit " + name + " is incompatible with unit " + unit.name);
186            }
187            return value * unit.getScale() / scale;
188        }
189    
190        public boolean isPrimary() {
191            return primary == this;
192        }
193    
194        public boolean isCompatible(Unit unit) {
195            return primary == unit.getPrimary();
196        }
197    
198        public Unit getPrimary() {
199            return this.primary;
200        }
201    
202        public int compareTo(Unit o) {
203            return scale < o.scale ? -1 : 1;
204        }
205    
206        public String toString() {
207            return name;
208        }
209    
210        /**
211         * @see java.lang.Object#hashCode()
212         */
213        @Override
214        public int hashCode() {
215            final int prime = 31;
216            int result = 1;
217            result = prime * result + ((name == null) ? 0 : name.hashCode());
218            return result;
219        }
220    
221        /**
222         * @see java.lang.Object#equals(java.lang.Object)
223         */
224        @Override
225        public boolean equals(Object obj) {
226            if (this == obj)
227                return true;
228            if (obj == null)
229                return false;
230            if (getClass() != obj.getClass())
231                return false;
232            final Unit other = (Unit) obj;
233            if (name == null) {
234                if (other.name != null)
235                    return false;
236            } else if (!name.equals(other.name))
237                return false;
238            return true;
239        }
240    
241    }