MetricsFactory.java
/*
* Copyright (c) 2001-2024, Jean Tessier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Jean Tessier nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jeantessier.metrics;
import org.apache.logging.log4j.*;
import org.apache.oro.text.perl.Perl5Util;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import static java.util.stream.Collectors.toCollection;
public class MetricsFactory {
private static final String EOL = System.getProperty("line.separator", "\n");
private static final Perl5Util perl = new Perl5Util();
private final String projectName;
private final MetricsConfiguration configuration;
private final Map<String, Metrics> projects = new HashMap<>();
private final Map<String, Metrics> groups = new HashMap<>();
private final Map<String, Metrics> classes = new HashMap<>();
private final Map<String, Metrics> methods = new HashMap<>();
private final Map<String, Metrics> includedProjects = new HashMap<>();
private final Map<String, Metrics> includedGroups = new HashMap<>();
private final Map<String, Metrics> includedClasses = new HashMap<>();
private final Map<String, Metrics> includedMethods = new HashMap<>();
private final WordCounter counter = new WordCounter();
public MetricsFactory(String projectName, MetricsConfiguration configuration) {
this.projectName = projectName;
this.configuration = configuration;
}
public String getProjectName() {
return projectName;
}
public MetricsConfiguration getConfiguration() {
return configuration;
}
public Metrics createProjectMetrics() {
return createProjectMetrics(getProjectName());
}
public Metrics createProjectMetrics(String name) {
Metrics result = projects.get(name);
if (result == null) {
result = buildProjectMetrics(name);
projects.put(name, result);
}
return result;
}
private Metrics buildProjectMetrics(String name) {
Metrics result = new Metrics(name);
populateMetrics(result, getConfiguration().getProjectMeasurements());
return result;
}
public void includeProjectMetrics(Metrics metrics) {
includedProjects.put(metrics.getName(), metrics);
}
public Collection<String> getProjectNames() {
return Collections.unmodifiableCollection(includedProjects.keySet());
}
public Collection<Metrics> getProjectMetrics() {
return Collections.unmodifiableCollection(includedProjects.values());
}
public Collection<String> getAllProjectNames() {
return Collections.unmodifiableCollection(projects.keySet());
}
public Collection<Metrics> getAllProjectMetrics() {
return Collections.unmodifiableCollection(projects.values());
}
public Metrics createGroupMetrics(String name) {
Metrics result = groups.get(name);
if (result == null) {
result = buildGroupMetrics(name);
groups.put(name, result);
}
return result;
}
private Metrics buildGroupMetrics(String name) {
Metrics projectMetrics = createProjectMetrics();
Metrics result = new Metrics(projectMetrics, name);
populateMetrics(result, getConfiguration().getGroupMeasurements());
initializeGroupMetrics(name, result);
return result;
}
private void initializeGroupMetrics(String packageName, Metrics metrics) {
computePackageNameCharacterCount(packageName, metrics);
computePackageNameWordCount(packageName, metrics);
}
private void computePackageNameCharacterCount(String packageName, Metrics metrics) {
metrics.addToMeasurement(BasicMeasurements.GROUP_NAME_CHARACTER_COUNT, packageName.length());
}
private void computePackageNameWordCount(String packageName, Metrics metrics) {
metrics.addToMeasurement(BasicMeasurements.GROUP_NAME_WORD_COUNT, counter.countPackageName(packageName));
}
public void includeGroupMetrics(Metrics metrics) {
includedGroups.put(metrics.getName(), metrics);
metrics.getParent().addSubMetrics(metrics);
includeProjectMetrics(metrics.getParent());
}
public Collection<String> getGroupNames() {
return Collections.unmodifiableCollection(includedGroups.keySet());
}
public Collection<Metrics> getGroupMetrics() {
return Collections.unmodifiableCollection(includedGroups.values());
}
public Collection<Metrics> getGroupMetrics(String className) {
return getConfiguration().getGroups(className).stream()
.map(this::createGroupMetrics)
.collect(toCollection(HashSet::new));
}
public Collection<String> getAllGroupNames() {
return Collections.unmodifiableCollection(groups.keySet());
}
public Collection<Metrics> getAllGroupMetrics() {
return Collections.unmodifiableCollection(groups.values());
}
public Metrics createClassMetrics(String name) {
Metrics result = classes.get(name);
if (result == null) {
result = buildClassMetrics(name);
classes.put(name, result);
}
return result;
}
private Metrics buildClassMetrics(String name) {
String packageName = "";
int pos = name.lastIndexOf('.');
if (pos != -1) {
packageName = name.substring(0, pos);
}
String className = name.substring(pos + 1);
Metrics packageMetrics = createGroupMetrics(packageName);
Metrics result = new Metrics(packageMetrics, name);
populateMetrics(result, getConfiguration().getClassMeasurements());
initializeClassMetrics(className, result);
return result;
}
private void initializeClassMetrics(String className, Metrics metrics) {
computeClassNameCharacterCount(className, metrics);
computeClassNameWordCount(className, metrics);
}
private void computeClassNameCharacterCount(String className, Metrics metrics) {
metrics.addToMeasurement(BasicMeasurements.CLASS_NAME_CHARACTER_COUNT, className.length());
}
private void computeClassNameWordCount(String className, Metrics metrics) {
metrics.addToMeasurement(BasicMeasurements.CLASS_NAME_WORD_COUNT, counter.countIdentifier(className));
}
public void includeClassMetrics(Metrics metrics) {
includedClasses.put(metrics.getName(), metrics);
metrics.getParent().addSubMetrics(metrics);
includeGroupMetrics(metrics.getParent());
getConfiguration().getGroups(metrics.getName()).forEach(name -> {
Metrics groupMetrics = createGroupMetrics(name);
groupMetrics.addSubMetrics(metrics);
includeGroupMetrics(groupMetrics);
});
}
public Collection<String> getClassNames() {
return Collections.unmodifiableCollection(includedClasses.keySet());
}
public Collection<Metrics> getClassMetrics() {
return Collections.unmodifiableCollection(includedClasses.values());
}
public Collection<String> getAllClassNames() {
return Collections.unmodifiableCollection(classes.keySet());
}
public Collection<Metrics> getAllClassMetrics() {
return Collections.unmodifiableCollection(classes.values());
}
public Metrics createMethodMetrics(String name) {
var result = methods.get(name);
if (result == null) {
result = buildMethodMetrics(name);
methods.put(name, result);
}
return result;
}
private Metrics buildMethodMetrics(String name) {
String className = "";
String featureName = "";
if (perl.match("/^(.*)\\.([^\\.]*)\\(.*\\)(: \\S.*)?$/", name)) {
className = perl.group(1);
featureName = perl.group(2);
} else if (perl.match("/^(.*)\\.(static) {}(: \\S.*)?$/", name)) {
className = perl.group(1);
featureName = perl.group(2);
} else if (perl.match("/^(.*)\\.([\\^.]*)$/", name)) {
className = perl.group(1);
featureName = perl.group(2);
}
Metrics classMetrics = createClassMetrics(className);
Metrics result = new Metrics(classMetrics, name);
classMetrics.addSubMetrics(result);
populateMetrics(result, getConfiguration().getMethodMeasurements());
initializeMethodMetrics(featureName, result);
return result;
}
private void initializeMethodMetrics(String featureName, Metrics metrics) {
computeMethodNameCharacterCount(featureName, metrics);
computeMethodNameWordCount(featureName, metrics);
}
private void computeMethodNameCharacterCount(String featureName, Metrics metrics) {
metrics.addToMeasurement(BasicMeasurements.METHOD_NAME_CHARACTER_COUNT, featureName.length());
}
private void computeMethodNameWordCount(String featureName, Metrics metrics) {
metrics.addToMeasurement(BasicMeasurements.METHOD_NAME_WORD_COUNT, counter.countIdentifier(featureName));
}
public void includeMethodMetrics(Metrics metrics) {
includedMethods.put(metrics.getName(), metrics);
metrics.getParent().addSubMetrics(metrics);
includeClassMetrics(metrics.getParent());
}
public Collection<String> getMethodNames() {
return includedMethods.values().stream()
.map(Metrics::getName)
.toList();
}
public Collection<Metrics> getMethodMetrics() {
return Collections.unmodifiableCollection(includedMethods.values());
}
public Collection<String> getAllMethodNames() {
return methods.values().stream()
.map(Metrics::getName)
.toList();
}
public Collection<Metrics> getAllMethodMetrics() {
return Collections.unmodifiableCollection(methods.values());
}
public void clear() {
projects.clear();
groups.clear();
classes.clear();
methods.clear();
includedProjects.clear();
includedGroups.clear();
includedClasses.clear();
includedMethods.clear();
}
private void populateMetrics(Metrics metrics, Collection<MeasurementDescriptor> descriptors) {
descriptors.forEach(descriptor -> {
try {
metrics.track(descriptor.createMeasurement(metrics));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
LogManager.getLogger(getClass()).warn("\"{}\" is unable to track \"{}\"", metrics.getName(), descriptor.getShortName(), ex);
}
});
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Factory for project \"").append(getProjectName()).append("\"").append(EOL);
builder.append("projects:").append(EOL);
projects.forEach((name, metrics) -> builder.append(" ").append(name).append(" -> ").append(metrics.getName()).append(EOL));
builder.append("groups:").append(EOL);
groups.forEach((name, metrics) -> builder.append(" ").append(name).append(" -> ").append(metrics.getName()).append(EOL));
builder.append("classes:").append(EOL);
classes.forEach((name, metrics) -> builder.append(" ").append(name).append(" -> ").append(metrics.getName()).append(EOL));
builder.append("methods:").append(EOL);
methods.forEach((name, metrics) -> builder.append(" ").append(name).append(" -> ").append(metrics.getName()).append(EOL));
return builder.toString();
}
}