You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Metric.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.api.measures;
  21. import java.io.Serializable;
  22. import java.util.Arrays;
  23. import java.util.List;
  24. import java.util.stream.Collectors;
  25. import javax.annotation.CheckForNull;
  26. import javax.annotation.Nullable;
  27. import org.apache.commons.lang.builder.ReflectionToStringBuilder;
  28. import org.apache.commons.lang.builder.ToStringStyle;
  29. import org.sonar.api.ce.ComputeEngineSide;
  30. import org.sonar.api.scanner.ScannerSide;
  31. import org.sonar.api.server.ServerSide;
  32. import static org.apache.commons.lang.StringUtils.isNotBlank;
  33. import static org.sonar.api.utils.Preconditions.checkArgument;
  34. /**
  35. * Used to define a metric in a plugin. Should be used with {@link Metrics} extension point.
  36. */
  37. @ScannerSide
  38. @ServerSide
  39. @ComputeEngineSide
  40. public class Metric<G extends Serializable> implements Serializable, org.sonar.api.batch.measure.Metric<G> {
  41. /**
  42. * @since 5.3
  43. */
  44. public static final int DEFAULT_DECIMAL_SCALE = 1;
  45. /**
  46. * The maximum supported value of scale for decimal metrics
  47. *
  48. * @since 5.3
  49. */
  50. public static final int MAX_DECIMAL_SCALE = 5;
  51. /**
  52. * A metric bigger value means a degradation
  53. */
  54. public static final int DIRECTION_WORST = -1;
  55. /**
  56. * A metric bigger value means an improvement
  57. */
  58. public static final int DIRECTION_BETTER = 1;
  59. /**
  60. * The metric direction has no meaning
  61. */
  62. public static final int DIRECTION_NONE = 0;
  63. public enum ValueType {
  64. INT(Integer.class),
  65. FLOAT(Double.class),
  66. PERCENT(Double.class),
  67. BOOL(Boolean.class),
  68. STRING(String.class),
  69. MILLISEC(Long.class),
  70. DATA(String.class),
  71. LEVEL(Metric.Level.class),
  72. DISTRIB(String.class),
  73. RATING(Integer.class),
  74. WORK_DUR(Long.class);
  75. private final Class valueClass;
  76. ValueType(Class valueClass) {
  77. this.valueClass = valueClass;
  78. }
  79. private Class valueType() {
  80. return valueClass;
  81. }
  82. public static String[] names() {
  83. ValueType[] values = values();
  84. String[] names = new String[values.length];
  85. for (int i = 0; i < values.length; i += 1) {
  86. names[i] = values[i].name();
  87. }
  88. return names;
  89. }
  90. }
  91. public enum Level {
  92. OK("Green"),
  93. /**
  94. * @deprecated in 7.6.
  95. */
  96. @Deprecated
  97. WARN("Orange"),
  98. ERROR("Red");
  99. private static final List<String> NAMES = Arrays.stream(values())
  100. .map(Level::name)
  101. .collect(Collectors.toList());
  102. private String colorName;
  103. Level(String colorName) {
  104. this.colorName = colorName;
  105. }
  106. public String getColorName() {
  107. return colorName;
  108. }
  109. public static List<String> names() {
  110. return NAMES;
  111. }
  112. }
  113. private String uuid;
  114. private String key;
  115. private String description;
  116. private ValueType type;
  117. private Integer direction;
  118. private String domain;
  119. private String name;
  120. private Boolean qualitative = Boolean.FALSE;
  121. private Boolean userManaged = Boolean.FALSE;
  122. private Boolean enabled = Boolean.TRUE;
  123. private Double worstValue;
  124. private Double bestValue;
  125. private Boolean optimizedBestValue;
  126. private Boolean hidden = Boolean.FALSE;
  127. private Boolean deleteHistoricalData;
  128. private Integer decimalScale;
  129. private Metric(Builder builder) {
  130. this.key = builder.key;
  131. this.name = builder.name;
  132. this.description = builder.description;
  133. this.type = builder.type;
  134. this.direction = builder.direction;
  135. this.domain = builder.domain;
  136. this.qualitative = builder.qualitative;
  137. this.enabled = Boolean.TRUE;
  138. this.worstValue = builder.worstValue;
  139. this.optimizedBestValue = builder.optimizedBestValue;
  140. this.bestValue = builder.bestValue;
  141. this.hidden = builder.hidden;
  142. this.userManaged = builder.userManaged;
  143. this.deleteHistoricalData = builder.deleteHistoricalData;
  144. this.decimalScale = builder.decimalScale;
  145. }
  146. /**
  147. * Creates an empty metric
  148. *
  149. * @deprecated in 1.12. Use the {@link Builder} factory.
  150. */
  151. @Deprecated
  152. public Metric() {
  153. }
  154. /**
  155. * Creates a metric based on its key. Shortcut to Metric(key, ValueType.INT)
  156. *
  157. * @param key the metric key
  158. * @deprecated since 2.7 use the {@link Builder} factory.
  159. */
  160. @Deprecated
  161. public Metric(String key) {
  162. this(key, ValueType.INT);
  163. }
  164. /**
  165. * Creates a metric based on a key and a type. Shortcut to
  166. * Metric(key, key, key, type, -1, Boolean.FALSE, null, false)
  167. *
  168. * @param key the key
  169. * @param type the type
  170. * @deprecated since 2.7 use the {@link Builder} factory.
  171. */
  172. @Deprecated
  173. public Metric(String key, ValueType type) {
  174. this(key, key, key, type, -1, Boolean.FALSE, null, false);
  175. }
  176. /**
  177. * @deprecated since 2.7 use the {@link Builder} factory.
  178. */
  179. @Deprecated
  180. public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain) {
  181. this(key, name, description, type, direction, qualitative, domain, false);
  182. }
  183. /**
  184. * Creates a fully qualified metric.
  185. *
  186. * @param key the metric key
  187. * @param name the metric name
  188. * @param description the metric description
  189. * @param type the metric type
  190. * @param direction the metric direction
  191. * @param qualitative whether the metric is qualitative
  192. * @param domain the metric domain
  193. * @param userManaged whether the metric is user managed
  194. */
  195. private Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain,
  196. boolean userManaged) {
  197. this.key = key;
  198. this.description = description;
  199. this.type = type;
  200. this.direction = direction;
  201. this.domain = domain;
  202. this.name = name;
  203. this.qualitative = qualitative;
  204. this.userManaged = userManaged;
  205. if (ValueType.PERCENT == this.type) {
  206. this.bestValue = (direction == DIRECTION_BETTER) ? 100.0 : 0.0;
  207. this.worstValue = (direction == DIRECTION_BETTER) ? 0.0 : 100.0;
  208. this.decimalScale = DEFAULT_DECIMAL_SCALE;
  209. } else if (ValueType.FLOAT == this.type) {
  210. this.decimalScale = DEFAULT_DECIMAL_SCALE;
  211. }
  212. }
  213. /**
  214. * For internal use only
  215. */
  216. public String getUuid() {
  217. return uuid;
  218. }
  219. /**
  220. * For internal use only
  221. */
  222. public Metric<G> setUuid(@Nullable String uuid) {
  223. this.uuid = uuid;
  224. return this;
  225. }
  226. /**
  227. * @return wether the metric is qualitative
  228. */
  229. public Boolean getQualitative() {
  230. return qualitative;
  231. }
  232. /**
  233. * Sets whether the metric is qualitative
  234. *
  235. * @param qualitative whether the metric is qualitative
  236. * @return this
  237. */
  238. public Metric<G> setQualitative(Boolean qualitative) {
  239. this.qualitative = qualitative;
  240. return this;
  241. }
  242. /**
  243. * @return the metric key
  244. */
  245. public String getKey() {
  246. return key;
  247. }
  248. /**
  249. * Sets the metric key
  250. *
  251. * @param key the key
  252. * @return this
  253. */
  254. public Metric<G> setKey(String key) {
  255. this.key = key;
  256. return this;
  257. }
  258. /**
  259. * @return the metric type
  260. */
  261. public ValueType getType() {
  262. return type;
  263. }
  264. /**
  265. * Sets the metric type
  266. *
  267. * @param type the type
  268. * @return this
  269. */
  270. public Metric<G> setType(ValueType type) {
  271. this.type = type;
  272. return this;
  273. }
  274. /**
  275. * @return the metric description
  276. */
  277. @CheckForNull
  278. public String getDescription() {
  279. return description;
  280. }
  281. /**
  282. * Sets the metric description
  283. *
  284. * @param description the description
  285. * @return this
  286. */
  287. public Metric<G> setDescription(@Nullable String description) {
  288. this.description = description;
  289. return this;
  290. }
  291. /**
  292. * @return whether the metric is a managed by the users ("manual metric")
  293. */
  294. @Deprecated
  295. public Boolean getUserManaged() {
  296. return userManaged;
  297. }
  298. /**
  299. * Sets whether the metric is managed by users ("manual metric")
  300. *
  301. * @param userManaged whether the metric is user managed
  302. * @return this
  303. */
  304. public Metric<G> setUserManaged(Boolean userManaged) {
  305. this.userManaged = userManaged;
  306. return this;
  307. }
  308. /**
  309. * @return whether the metric is enabled
  310. */
  311. public Boolean getEnabled() {
  312. return enabled;
  313. }
  314. /**
  315. * Sets whether the metric is enabled
  316. *
  317. * @param enabled whether the metric is enabled
  318. * @return this
  319. */
  320. public Metric<G> setEnabled(Boolean enabled) {
  321. this.enabled = enabled;
  322. return this;
  323. }
  324. /**
  325. * @return the metric direction
  326. */
  327. public Integer getDirection() {
  328. return direction;
  329. }
  330. /**
  331. * Sets the metric direction.
  332. *
  333. * @param direction the direction
  334. */
  335. public Metric<G> setDirection(Integer direction) {
  336. this.direction = direction;
  337. return this;
  338. }
  339. /**
  340. * @return the domain of the metric
  341. */
  342. public String getDomain() {
  343. return domain;
  344. }
  345. /**
  346. * Sets the domain for the metric (General, Complexity...)
  347. *
  348. * @param domain the domain
  349. * @return this
  350. */
  351. public Metric<G> setDomain(String domain) {
  352. this.domain = domain;
  353. return this;
  354. }
  355. /**
  356. * @return the metric name
  357. */
  358. public String getName() {
  359. return name;
  360. }
  361. /**
  362. * Sets the metric name
  363. *
  364. * @param name the name
  365. * @return this
  366. */
  367. public Metric<G> setName(String name) {
  368. this.name = name;
  369. return this;
  370. }
  371. public Double getWorstValue() {
  372. return worstValue;
  373. }
  374. @CheckForNull
  375. public Double getBestValue() {
  376. return bestValue;
  377. }
  378. /**
  379. * @return this
  380. */
  381. public Metric<G> setWorstValue(@Nullable Double d) {
  382. this.worstValue = d;
  383. return this;
  384. }
  385. /**
  386. * @param bestValue the best value. It can be null.
  387. * @return this
  388. */
  389. public Metric<G> setBestValue(@Nullable Double bestValue) {
  390. this.bestValue = bestValue;
  391. return this;
  392. }
  393. /**
  394. * @return whether the metric is of a numeric type (int, percentage...)
  395. */
  396. public boolean isNumericType() {
  397. return ValueType.INT.equals(type)
  398. || ValueType.FLOAT.equals(type)
  399. || ValueType.PERCENT.equals(type)
  400. || ValueType.BOOL.equals(type)
  401. || ValueType.MILLISEC.equals(type)
  402. || ValueType.RATING.equals(type)
  403. || ValueType.WORK_DUR.equals(type);
  404. }
  405. /**
  406. * @return whether the metric is of type data
  407. */
  408. public boolean isDataType() {
  409. return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type);
  410. }
  411. /**
  412. * @return whether the metric is of type percentage
  413. */
  414. public boolean isPercentageType() {
  415. return ValueType.PERCENT.equals(type);
  416. }
  417. public Metric<G> setOptimizedBestValue(@Nullable Boolean b) {
  418. this.optimizedBestValue = b;
  419. return this;
  420. }
  421. /**
  422. * @return null for manual metrics
  423. */
  424. @CheckForNull
  425. public Boolean isOptimizedBestValue() {
  426. return optimizedBestValue;
  427. }
  428. public Boolean isHidden() {
  429. return hidden;
  430. }
  431. public Metric<G> setHidden(Boolean hidden) {
  432. this.hidden = hidden;
  433. return this;
  434. }
  435. public Boolean getDeleteHistoricalData() {
  436. return deleteHistoricalData;
  437. }
  438. /**
  439. * Return the number scale if metric type is {@link ValueType#FLOAT}, else {@code null}
  440. *
  441. * @since 5.3
  442. */
  443. @CheckForNull
  444. public Integer getDecimalScale() {
  445. return decimalScale;
  446. }
  447. @Override
  448. public int hashCode() {
  449. return key.hashCode();
  450. }
  451. @Override
  452. public boolean equals(Object obj) {
  453. if (!(obj instanceof Metric)) {
  454. return false;
  455. }
  456. if (this == obj) {
  457. return true;
  458. }
  459. Metric other = (Metric) obj;
  460. return key.equals(other.getKey());
  461. }
  462. @Override
  463. public String toString() {
  464. return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
  465. }
  466. /**
  467. * Merge with fields from other metric. All fields are copied, except the id.
  468. *
  469. * @return this
  470. */
  471. public Metric<G> merge(final Metric with) {
  472. this.description = with.description;
  473. this.domain = with.domain;
  474. this.enabled = with.enabled;
  475. this.qualitative = with.qualitative;
  476. this.worstValue = with.worstValue;
  477. this.bestValue = with.bestValue;
  478. this.optimizedBestValue = with.optimizedBestValue;
  479. this.direction = with.direction;
  480. this.key = with.key;
  481. this.type = with.type;
  482. this.name = with.name;
  483. this.userManaged = with.userManaged;
  484. this.hidden = with.hidden;
  485. this.deleteHistoricalData = with.deleteHistoricalData;
  486. return this;
  487. }
  488. /**
  489. * Metric.Builder is used to create metric definitions. It must be preferred to creating new instances of the Metric class directly.
  490. *
  491. * @since 2.7
  492. */
  493. public static final class Builder {
  494. private String key;
  495. private Metric.ValueType type;
  496. private String name;
  497. private String description;
  498. private Integer direction = DIRECTION_NONE;
  499. private Boolean qualitative = Boolean.FALSE;
  500. private String domain = null;
  501. private Double worstValue;
  502. private Double bestValue;
  503. private boolean optimizedBestValue = false;
  504. private boolean hidden = false;
  505. private boolean userManaged = false;
  506. private boolean deleteHistoricalData = false;
  507. private Integer decimalScale = null;
  508. /**
  509. * Creates a new {@link Builder} object.
  510. *
  511. * @param key the metric key, should be unique among all metrics
  512. * @param name the metric name
  513. * @param type the metric type
  514. */
  515. public Builder(String key, String name, ValueType type) {
  516. checkArgument(isNotBlank(key), "Metric key can not be blank");
  517. checkArgument(isNotBlank(name), "Name of metric %s must be set", key);
  518. checkArgument(type != null, "Type of metric %s must be set", key);
  519. this.key = key;
  520. this.name = name;
  521. this.type = type;
  522. }
  523. /**
  524. * Sets the metric description.
  525. *
  526. * @param d the description
  527. * @return the builder
  528. */
  529. public Builder setDescription(String d) {
  530. this.description = d;
  531. return this;
  532. }
  533. /**
  534. * Sets the metric direction (used for numeric values only), which is used in the Web UI to show if the trend of a metric is good or not.
  535. * <ul>
  536. * <li>Metric.DIRECTION_WORST: indicates that an increase of the metric value is not a good thing (example: the complexity of a function)</li>
  537. * <li>Metric.DIRECTION_BETTER: indicates that an increase of the metric value is a good thing (example: the code coverage of a function)</li>
  538. * <li>Metric.DIRECTION_NONE: indicates that the variation of the metric value is neither good nor bad (example: number of files).</li>
  539. * </ul>
  540. * Metric.DIRECTION_NONE is the default value.
  541. *
  542. * @param d the direction
  543. * @return the builder
  544. * @see Metric#DIRECTION_WORST
  545. * @see Metric#DIRECTION_BETTER
  546. * @see Metric#DIRECTION_NONE
  547. */
  548. public Builder setDirection(Integer d) {
  549. this.direction = d;
  550. return this;
  551. }
  552. /**
  553. * Sets whether the metric is qualitative or not. Default value is false.
  554. * <br>
  555. * If set to true, then variations of this metric will be highlighted in the Web UI (for instance, trend icons will be red or green instead of default grey).
  556. *
  557. * @param b Boolean.TRUE if the metric is qualitative
  558. * @return the builder
  559. */
  560. public Builder setQualitative(Boolean b) {
  561. this.qualitative = b;
  562. return this;
  563. }
  564. /**
  565. * Sets the domain for the metric (General, Complexity...). This is used to group metrics in the Web UI.
  566. * <br>
  567. * By default, the metric belongs to no specific domain.
  568. *
  569. * @param d the domain
  570. * @return the builder
  571. */
  572. public Builder setDomain(String d) {
  573. this.domain = d;
  574. return this;
  575. }
  576. /**
  577. * Sets the worst value that the metric can get (example: 0.0 for code coverage). No worst value is set by default.
  578. *
  579. * @param d the worst value
  580. * @return the builder
  581. */
  582. public Builder setWorstValue(Double d) {
  583. this.worstValue = d;
  584. return this;
  585. }
  586. /**
  587. * Sets the best value that the metric can get (example: 100.0 for code coverage). No best value is set by default.
  588. * <br>
  589. * Resources would be hidden on drilldown page, if the value of measure equals to best value.
  590. *
  591. * @param d the best value
  592. * @return the builder
  593. */
  594. public Builder setBestValue(Double d) {
  595. this.bestValue = d;
  596. return this;
  597. }
  598. /**
  599. * Specifies whether file-level measures that equal to the defined best value are stored or not. Default is false.
  600. * <br>
  601. * Example with the metric that stores the number of violation ({@link CoreMetrics#VIOLATIONS}):
  602. * if a file has no violation, then the value '0' won't be stored in the database.
  603. *
  604. * @param b true if the measures must not be stored when they equal to the best value
  605. * @return the builder
  606. */
  607. public Builder setOptimizedBestValue(boolean b) {
  608. this.optimizedBestValue = b;
  609. return this;
  610. }
  611. /**
  612. * Sets whether the metric should be hidden in Web UI. Default is false.
  613. *
  614. * @param b true if the metric should be hidden.
  615. * @return the builder
  616. */
  617. public Builder setHidden(boolean b) {
  618. this.hidden = b;
  619. return this;
  620. }
  621. /**
  622. * Specifies whether this metric can be edited online in the "Manual measures" page. Default is false.
  623. *
  624. * @param b true if the metric can be edited online.
  625. * @return the builder
  626. * @since 2.10
  627. */
  628. @Deprecated
  629. public Builder setUserManaged(boolean b) {
  630. this.userManaged = b;
  631. return this;
  632. }
  633. /**
  634. * Specifies whether measures from the past can be automatically deleted to minimize database volume.
  635. * <br>
  636. * By default, historical data are kept.
  637. *
  638. * @param b true if measures from the past can be deleted automatically.
  639. * @return the builder
  640. * @since 2.14
  641. */
  642. public Builder setDeleteHistoricalData(boolean b) {
  643. this.deleteHistoricalData = b;
  644. return this;
  645. }
  646. /**
  647. * Scale to be used if the metric has decimal type ({@link ValueType#FLOAT} or {@link ValueType#PERCENT}).
  648. * Default is 1. It is not set (({@code null}) on non-decimal metrics.
  649. *
  650. * @since 5.3
  651. */
  652. public Builder setDecimalScale(int scale) {
  653. checkArgument(scale >= 0, "Scale of decimal metric %s must be positive: %d", key, scale);
  654. checkArgument(scale <= MAX_DECIMAL_SCALE, "Scale of decimal metric [%s] must be less than or equal %s: %s", key, MAX_DECIMAL_SCALE, scale);
  655. this.decimalScale = scale;
  656. return this;
  657. }
  658. /**
  659. * Creates a new metric definition based on the properties set on this metric builder.
  660. *
  661. * @return a new {@link Metric} object
  662. */
  663. public <G extends Serializable> Metric<G> create() {
  664. if (ValueType.PERCENT == this.type) {
  665. this.bestValue = (direction == DIRECTION_BETTER) ? 100.0 : 0.0;
  666. this.worstValue = (direction == DIRECTION_BETTER) ? 0.0 : 100.0;
  667. this.decimalScale = coalesce(decimalScale, DEFAULT_DECIMAL_SCALE);
  668. } else if (ValueType.FLOAT == this.type) {
  669. this.decimalScale = coalesce(decimalScale, DEFAULT_DECIMAL_SCALE);
  670. }
  671. return new Metric<>(this);
  672. }
  673. }
  674. @CheckForNull
  675. private static <T> T coalesce(@Nullable T a, @Nullable T b) {
  676. return a == null ? b : a;
  677. }
  678. @Override
  679. public String key() {
  680. return getKey();
  681. }
  682. @Override
  683. public Class<G> valueType() {
  684. return getType().valueType();
  685. }
  686. }