blob: 0a370582e6c8488e87bbe4990a79d3201b8c6e7a [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.android.tools.lint;
import com.android.annotations.NonNull;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.Variant;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* A {@link Warning} represents a specific warning that a {@link LintClient}
* has been told about. The context stores these as they are reported into a
* list of warnings such that it can sort them all before presenting them all at
* the end.
*/
public class Warning implements Comparable<Warning> {
public final Issue issue;
public final String message;
public final Severity severity;
public final Project project;
public AndroidProject gradleProject;
public Location location;
public File file;
public String path;
public int line = -1;
public int offset = -1;
public String errorLine;
public String fileContents;
public Set<Variant> variants;
public Warning(Issue issue, String message, Severity severity, Project project) {
this.issue = issue;
this.message = message;
this.severity = severity;
this.project = project;
}
// ---- Implements Comparable<Warning> ----
@SuppressWarnings({"VariableNotUsedInsideIf", "ConstantConditions"})
@Override
public int compareTo(@NonNull Warning other) {
// Sort by category, then by priority, then by id,
// then by file, then by line
int categoryDelta = issue.getCategory().compareTo(other.issue.getCategory());
if (categoryDelta != 0) {
return categoryDelta;
}
// DECREASING priority order
int priorityDelta = other.issue.getPriority() - issue.getPriority();
if (priorityDelta != 0) {
return priorityDelta;
}
String id1 = issue.getId();
String id2 = other.issue.getId();
assert id1 != null;
assert id2 != null;
int idDelta = id1.compareTo(id2);
if (idDelta != 0) {
return idDelta;
}
if (file != null) {
if (other.file != null) {
int fileDelta = file.getName().compareTo(
other.file.getName());
if (fileDelta != 0) {
return fileDelta;
}
} else {
return -1;
}
} else if (other.file != null) {
return 1;
}
if (line != other.line) {
return line - other.line;
}
int delta = message.compareTo(other.message);
if (delta != 0) {
return delta;
}
if (file != null) {
if (other.file != null) {
delta = file.compareTo(other.file);
if (delta != 0) {
return delta;
}
} else {
return -1;
}
} else if (other.file != null) {
return 1;
}
Location secondary1 = location != null ? location.getSecondary() : null;
File secondaryFile1 = secondary1 != null ? secondary1.getFile() : null;
Location secondary2 = other.location != null ? other.location.getSecondary() : null;
File secondaryFile2 = secondary2 != null ? secondary2.getFile() : null;
if (secondaryFile1 != null) {
if (secondaryFile2 != null) {
return secondaryFile1.compareTo(secondaryFile2);
} else {
return -1;
}
} else if (secondaryFile2 != null) {
return 1;
}
// This handles the case where you have a huge XML document without hewlines,
// such that all the errors end up on the same line.
if (location != null && other.location != null &&
location.getStart() != null && other.location.getStart() != null) {
delta = location.getStart().getColumn() - other.location.getStart().getColumn();
if (delta != 0) {
return delta;
}
}
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Warning warning = (Warning) o;
if (line != warning.line) {
return false;
}
if (file != null ? !file.equals(warning.file) : warning.file != null) {
return false;
}
if (!issue.getCategory().equals(warning.issue.getCategory())) {
return false;
}
if (issue.getPriority() != warning.issue.getPriority()) {
return false;
}
if (!issue.getId().equals(warning.issue.getId())) {
return false;
}
//noinspection RedundantIfStatement
if (!message.equals(warning.message)) {
return false;
}
Location secondary1 = location != null ? location.getSecondary() : null;
Location secondary2 = warning.location != null ? warning.location.getSecondary() : null;
if (secondary1 != null) {
if (secondary2 != null) {
if (!Objects.equal(secondary1.getFile(), secondary2.getFile())) {
return false;
}
} else {
return false;
}
} else //noinspection VariableNotUsedInsideIf
if (secondary2 != null) {
return false;
}
// This handles the case where you have a huge XML document without hewlines,
// such that all the errors end up on the same line.
//noinspection RedundantIfStatement
if (location != null && warning.location != null &&
location.getStart() != null && warning.location.getStart() != null &&
location.getStart().getColumn() != warning.location.getStart().getColumn()) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = message.hashCode();
result = 31 * result + (file != null ? file.hashCode() : 0);
return result;
}
public boolean isVariantSpecific() {
return variants != null && variants.size() < gradleProject.getVariants().size();
}
public boolean includesMoreThanExcludes() {
assert isVariantSpecific();
int variantCount = variants.size();
int allVariantCount = gradleProject.getVariants().size();
return variantCount <= allVariantCount - variantCount;
}
public List<String> getIncludedVariantNames() {
assert isVariantSpecific();
List<String> names = new ArrayList<String>();
if (variants != null) {
for (Variant variant : variants) {
names.add(variant.getName());
}
}
Collections.sort(names);
return names;
}
public List<String> getExcludedVariantNames() {
assert isVariantSpecific();
Collection<Variant> variants = gradleProject.getVariants();
Set<String> allVariants = new HashSet<String>(variants.size());
for (Variant variant : variants) {
allVariants.add(variant.getName());
}
Set<String> included = new HashSet<String>(getIncludedVariantNames());
Set<String> excluded = Sets.difference(allVariants, included);
List<String> sorted = Lists.newArrayList(excluded);
Collections.sort(sorted);
return sorted;
}
@Override
public String toString() {
return "Warning{" +
"issue=" + issue +
", message='" + message + '\'' +
", file=" + file +
", line=" + line +
'}';
}
}