/*
* Copyright 2005-2016 Sixth and Red River Software, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sixrr.metrics.metricModel;
import com.intellij.analysis.AnalysisScope;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiPackage;
import com.sixrr.metrics.Metric;
import com.sixrr.metrics.MetricCategory;
import com.sixrr.metrics.profile.MetricRepository;
import com.sixrr.metrics.profile.MetricsProfile;
import com.sixrr.metrics.profile.MetricsProfileRepository;
import com.sixrr.metrics.utils.MethodUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jetbrains.annotations.NotNull;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.*;
import java.util.*;
public class MetricsRunImpl implements MetricsRun {
private static final Logger logger = Logger.getInstance("MetricsReloaded");
private final Map<MetricCategory, MetricsResult> metricResults =
new EnumMap<MetricCategory, MetricsResult>(MetricCategory.class);
private String profileName = null;
private AnalysisScope context = null;
private TimeStamp timestamp = null;
public MetricsRunImpl() {
final MetricCategory[] categories = MetricCategory.values();
for (MetricCategory category : categories) {
metricResults.put(category, new MetricsResultImpl());
}
}
@Override
public List<Metric> getMetrics() {
final Set<Metric> allMetrics = new HashSet<Metric>();
final Collection<MetricsResult> results = metricResults.values();
for (MetricsResult result : results) {
final Metric[] metrics = result.getMetrics();
allMetrics.addAll(Arrays.asList(metrics));
}
return new ArrayList<Metric>(allMetrics);
}
@NotNull
private static String getFileTypeString(FileType fileType) {
final String description = fileType.getDescription();
return StringUtil.trimEnd(StringUtil.trimEnd(StringUtil.trimEnd(StringUtil.trimEnd(description,
" (syntax highlighting only)"), " files"), " Files"), " source");
}
@Override
public void postProjectMetric(@NotNull Metric metric, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.Project);
results.postValue(metric, "project", value);
}
@Override
public void postFileTypeMetric(Metric metric, FileType fileType, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.FileType);
results.postValue(metric, getFileTypeString(fileType), value);
}
@Override
public void postModuleMetric(@NotNull Metric metric, @NotNull Module module, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.Module);
results.postValue(metric, module.getName(), value);
}
@Override
public void postPackageMetric(@NotNull Metric metric, @NotNull PsiPackage aPackage, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.Package);
results.postValue(metric, aPackage.getQualifiedName(), value);
}
@Override
public void postClassMetric(@NotNull Metric metric, @NotNull PsiClass aClass, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.Class);
final String qualifiedName = aClass.getQualifiedName();
results.postValue(metric, qualifiedName, value);
results.setElementForMeasuredObject(qualifiedName, aClass);
}
@Override
public void postInterfaceMetric(@NotNull Metric metric, @NotNull PsiClass anInterface, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.Interface);
final String qualifiedName = anInterface.getQualifiedName();
results.postValue(metric, qualifiedName, value);
results.setElementForMeasuredObject(qualifiedName, anInterface);
}
@Override
public void postMethodMetric(@NotNull Metric metric, @NotNull PsiMethod method, double value) {
final MetricsResult results = getResultsForCategory(MetricCategory.Method);
final String signature = MethodUtils.calculateSignature(method);
results.postValue(metric, signature, value);
results.setElementForMeasuredObject(signature, method);
}
@Override
public void postProjectMetric(@NotNull Metric metric, double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.Project);
results.postValue(metric, "project", numerator, denominator);
}
@Override
public void postModuleMetric(@NotNull Metric metric, @NotNull Module module, double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.Module);
results.postValue(metric, module.getName(), numerator, denominator);
}
@Override
public void postPackageMetric(@NotNull Metric metric, @NotNull PsiPackage aPackage,
double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.Package);
results.postValue(metric, aPackage.getQualifiedName(), numerator, denominator);
}
@Override
public void postFileTypeMetric(@NotNull Metric metric, @NotNull FileType fileType,
double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.FileType);
results.postValue(metric, getFileTypeString(fileType), numerator, denominator);
}
@Override
public void postClassMetric(@NotNull Metric metric, @NotNull PsiClass aClass,
double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.Class);
results.postValue(metric, aClass.getQualifiedName(), numerator, denominator);
}
@Override
public void postInterfaceMetric(@NotNull Metric metric, @NotNull PsiClass anInterface,
double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.Interface);
results.postValue(metric, anInterface.getQualifiedName(), numerator, denominator);
}
@Override
public void postMethodMetric(@NotNull Metric metric, @NotNull PsiMethod method,
double numerator, double denominator) {
final MetricsResult results = getResultsForCategory(MetricCategory.Method);
final String signature = MethodUtils.calculateSignature(method);
results.postValue(metric, signature, numerator, denominator);
results.setElementForMeasuredObject(signature, method);
}
private void postRawMetric(@NotNull Metric metric, @NotNull String measured, double value) {
final MetricCategory category = metric.getCategory();
final MetricsResult result = metricResults.get(category);
result.postValue(metric, measured, value);
}
@Override
public MetricsResult getResultsForCategory(@NotNull MetricCategory category) {
return metricResults.get(category);
}
private void setResultsForCategory(@NotNull MetricCategory category, @NotNull MetricsResult results) {
metricResults.put(category, results);
}
@Override
public void writeToFile(@NotNull String fileName) {
try {
final XMLStreamWriter writer =
XMLOutputFactory.newInstance().createXMLStreamWriter(new FileOutputStream(fileName), "UTF-8");
try {
writer.writeStartDocument();
writer.writeCharacters("\n");
writer.writeStartElement("SNAPSHOT");
writer.writeAttribute("profile", profileName);
writer.writeAttribute("timestamp", timestamp.toString());
final String version = PluginManager.getPlugin(PluginId.getId("MetricsReloaded")).getVersion();
writer.writeAttribute("version", version);
writer.writeCharacters("\n");
final MetricCategory[] categories = MetricCategory.values();
for (MetricCategory category : categories) {
writeResultsForCategory(category, writer);
}
writer.writeEndElement();
writer.writeEndDocument();
} finally {
try {
writer.close();
} catch (XMLStreamException e) {
logger.warn(e);
}
}
} catch (IOException e) {
logger.warn(e);
} catch (XMLStreamException e) {
logger.warn(e);
}
}
@Override
public String getProfileName() {
return profileName;
}
public void setProfileName(String profileName) {
this.profileName = profileName;
}
public void setContext(AnalysisScope context) {
this.context = context;
}
public void setTimestamp(TimeStamp timestamp) {
this.timestamp = timestamp;
}
@Override
public TimeStamp getTimestamp() {
return timestamp;
}
@Override
public AnalysisScope getContext() {
return context;
}
@Override
public MetricsRun filterRowsWithoutWarnings(@NotNull MetricsProfile profile) {
final MetricsRunImpl out = new MetricsRunImpl();
out.context = context;
out.profileName = profileName;
out.timestamp = timestamp;
final Set<MetricCategory> categories = metricResults.keySet();
for (MetricCategory category : categories) {
final MetricsResult results = getResultsForCategory(category);
final MetricsResult filteredResults = results.filterRowsWithoutWarnings(profile);
out.setResultsForCategory(category, filteredResults);
}
return out;
}
private void writeResultsForCategory(MetricCategory category, XMLStreamWriter writer) throws XMLStreamException {
final MetricsResult results = getResultsForCategory(category);
final Metric[] metrics = results.getMetrics();
for (final Metric metric : metrics) {
writeResultsForMetric(metric, results, writer);
}
}
private static void writeResultsForMetric(Metric metric, MetricsResult results, XMLStreamWriter writer)
throws XMLStreamException {
final Class<?> metricClass = metric.getClass();
final String[] measuredObjects = results.getMeasuredObjects();
writer.writeCharacters(" ");
writer.writeStartElement("METRIC");
writer.writeAttribute("class_name", metricClass.getName());
writer.writeCharacters("\n");
for (final String measuredObject : measuredObjects) {
writeValue(results, metric, measuredObject, writer);
}
writer.writeCharacters(" ");
writer.writeEndElement();
writer.writeCharacters("\n");
}
private static void writeValue(MetricsResult results, Metric metric, String measuredObject, XMLStreamWriter writer)
throws XMLStreamException {
final Double value = results.getValueForMetric(metric, measuredObject);
if (value != null) {
writer.writeCharacters(" ");
writer.writeEmptyElement("VALUE");
writer.writeAttribute("measured", measuredObject);
writer.writeAttribute("value", value.toString());
writer.writeCharacters("\n");
}
}
public static MetricsRun readFromFile(@NotNull File file) {
final SAXBuilder builder = new SAXBuilder();
Document doc;
try {
doc = builder.build(file);
} catch (Exception e) {
try {
doc = builder.build(fixBrokenXml(file));
} catch (Exception e1) {
logger.warn(e);
return null;
}
}
final Element snapshotElement = doc.getRootElement();
final MetricsRunImpl run = new MetricsRunImpl();
run.setTimestamp(new TimeStamp(snapshotElement.getAttributeValue("timestamp")));
run.setProfileName(snapshotElement.getAttributeValue("profile"));
final String version = snapshotElement.getAttributeValue("version"); // may need this later
final List<Element> metrics = snapshotElement.getChildren("METRIC");
final MetricRepository repository = MetricsProfileRepository.getInstance();
for (final Element metricElement : metrics) {
readMetricElement(metricElement, repository, run);
}
return run;
}
@NotNull
private static Reader fixBrokenXml(@NotNull File file) throws IOException {
final String s = FileUtilRt.loadFile(file);
final StringBuilder sb = new StringBuilder();
boolean insideQuotes = false;
for (int i = 0, length = s.length(); i < length; i++) {
final int c = s.codePointAt(i);
if (c == '"') {
insideQuotes = !insideQuotes;
sb.appendCodePoint(c);
}
else if (!insideQuotes) {
sb.appendCodePoint(c);
}
else if (c == '<') {
sb.append("<");
} else {
sb.appendCodePoint(c);
}
}
return new StringReader(sb.toString());
}
private static void readMetricElement(Element metricElement, MetricRepository repository, MetricsRunImpl run) {
try {
final String className = metricElement.getAttributeValue("class_name");
final Metric metric = repository.getMetric(className);
if (metric != null) {
final List<Element> values = metricElement.getChildren("VALUE");
for (final Element valueElement : values) {
final String measured = valueElement.getAttributeValue("measured");
final String valueString = valueElement.getAttributeValue("value");
final double value = Double.parseDouble(valueString);
run.postRawMetric(metric, measured, value);
}
}
} catch (Exception e) {
logger.warn(e);
}
}
public boolean hasWarnings(@NotNull MetricsProfile profile) {
for (MetricCategory category : MetricCategory.values()) {
final MetricsResult result = metricResults.get(category);
if (result.hasWarnings(profile)) {
return true;
}
}
return false;
}
}