Limit.java revision 7d588f9eab46c11e7f8b01c8b268dd21320a3708
1/*******************************************************************************
2 * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Marc R. Hoffmann - initial API and implementation
10 *
11 *******************************************************************************/
12package org.jacoco.report.check;
13
14import java.math.BigDecimal;
15import java.math.RoundingMode;
16import java.util.Collections;
17import java.util.HashMap;
18import java.util.Map;
19
20import org.jacoco.core.analysis.ICounter.CounterValue;
21import org.jacoco.core.analysis.ICoverageNode;
22import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
23
24/**
25 * Descriptor for a limit which is given by a {@link Rule}.
26 */
27public class Limit {
28
29	private static final Map<CounterValue, String> VALUE_NAMES;
30	private static final Map<CounterEntity, String> ENTITY_NAMES;
31
32	static {
33		final Map<CounterValue, String> values = new HashMap<CounterValue, String>();
34		values.put(CounterValue.TOTALCOUNT, "total count");
35		values.put(CounterValue.MISSEDCOUNT, "missed count");
36		values.put(CounterValue.COVEREDCOUNT, "covered count");
37		values.put(CounterValue.MISSEDRATIO, "missed ratio");
38		values.put(CounterValue.COVEREDRATIO, "covered ratio");
39		VALUE_NAMES = Collections.unmodifiableMap(values);
40
41		final Map<CounterEntity, String> entities = new HashMap<CounterEntity, String>();
42		entities.put(CounterEntity.INSTRUCTION, "instructions");
43		entities.put(CounterEntity.BRANCH, "branches");
44		entities.put(CounterEntity.COMPLEXITY, "complexity");
45		entities.put(CounterEntity.LINE, "lines");
46		entities.put(CounterEntity.METHOD, "methods");
47		entities.put(CounterEntity.CLASS, "classes");
48		ENTITY_NAMES = Collections.unmodifiableMap(entities);
49	}
50
51	private CounterEntity entity;
52
53	private CounterValue value;
54
55	private BigDecimal minimum;
56
57	private BigDecimal maximum;
58
59	/**
60	 * Creates a new instance with the following defaults:
61	 * <ul>
62	 * <li>counter entity: {@link CounterEntity#INSTRUCTION}
63	 * <li>counter value: {@link CounterValue#COVEREDRATIO}
64	 * <li>minimum: no limit
65	 * <li>maximum: no limit
66	 * </ul>
67	 */
68	public Limit() {
69		this.entity = CounterEntity.INSTRUCTION;
70		this.value = CounterValue.COVEREDRATIO;
71	}
72
73	/**
74	 * @return the configured counter entity to check
75	 */
76	public CounterEntity getEntity() {
77		return entity;
78	}
79
80	/**
81	 * Sets the counter entity to check.
82	 *
83	 * @param entity
84	 *            counter entity to check
85	 */
86	public void setCounter(final CounterEntity entity) {
87		this.entity = entity;
88	}
89
90	/**
91	 * @return the configured value to check
92	 */
93	public CounterValue getValue() {
94		return value;
95	}
96
97	/**
98	 * Sets the value to check.
99	 *
100	 * @param value
101	 *            value to check
102	 */
103	public void setValue(final CounterValue value) {
104		this.value = value;
105	}
106
107	/**
108	 * @return configured minimum value, or <code>null</code> if no minimum is
109	 *         given
110	 */
111	public String getMinimum() {
112		return minimum == null ? null : minimum.toPlainString();
113	}
114
115	/**
116	 * Sets allowed minimum value as decimal string representation. The given
117	 * precision is also considered in error messages. Coverage ratios are given
118	 * in the range from 0.0 to 1.0.
119	 *
120	 * @param minimum
121	 *            allowed minimum or <code>null</code>, if no minimum should be
122	 *            checked
123	 */
124	public void setMinimum(final String minimum) {
125		this.minimum = minimum == null ? null : new BigDecimal(minimum);
126	}
127
128	/**
129	 * @return configured maximum value, or <code>null</code> if no maximum is
130	 *         given
131	 */
132	public String getMaximum() {
133		return maximum == null ? null : maximum.toPlainString();
134	}
135
136	/**
137	 * Sets allowed maximum value as decimal string representation. The given
138	 * precision is also considered in error messages. Coverage ratios are given
139	 * in the range from 0.0 to 1.0.
140	 *
141	 * @param maximum
142	 *            allowed maximum or <code>null</code>, if no maximum should be
143	 *            checked
144	 */
145	public void setMaximum(final String maximum) {
146		this.maximum = maximum == null ? null : new BigDecimal(maximum);
147	}
148
149	String check(final ICoverageNode node) {
150		final double d = node.getCounter(entity).getValue(value);
151		if (Double.isNaN(d)) {
152			return null;
153		}
154		final BigDecimal bd = BigDecimal.valueOf(d);
155		if (minimum != null && minimum.compareTo(bd) > 0) {
156			return message("minimum", bd, minimum, RoundingMode.FLOOR);
157		}
158		if (maximum != null && maximum.compareTo(bd) < 0) {
159			return message("maximum", bd, maximum, RoundingMode.CEILING);
160		}
161		return null;
162	}
163
164	private String message(final String minmax, final BigDecimal v,
165			final BigDecimal ref, final RoundingMode mode) {
166		final BigDecimal rounded = v.setScale(ref.scale(), mode);
167		return String.format("%s %s is %s, but expected %s is %s",
168				ENTITY_NAMES.get(entity), VALUE_NAMES.get(value),
169				rounded.toPlainString(), minmax, ref.toPlainString());
170	}
171
172}
173