blob: eb2f15e03a087d8986a8c0a9154f8341c088b2be [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.detector.api;
import static com.android.tools.lint.detector.api.TextFormat.RAW;
import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.Configuration;
import com.google.common.annotations.Beta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* An issue is a potential bug in an Android application. An issue is discovered
* by a {@link Detector}, and has an associated {@link Severity}.
* <p>
* Issues and detectors are separate classes because a detector can discover
* multiple different issues as it's analyzing code, and we want to be able to
* different severities for different issues, the ability to suppress one but
* not other issues from the same detector, and so on.
* <p/>
* <b>NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.</b>
*/
@Beta
public final class Issue implements Comparable<Issue> {
private final String mId;
private final String mBriefDescription;
private final String mExplanation;
private final Category mCategory;
private final int mPriority;
private final Severity mSeverity;
private Object mMoreInfoUrls;
private boolean mEnabledByDefault = true;
private Implementation mImplementation;
// Use factory methods
private Issue(
@NonNull String id,
@NonNull String shortDescription,
@NonNull String explanation,
@NonNull Category category,
int priority,
@NonNull Severity severity,
@NonNull Implementation implementation) {
assert !shortDescription.isEmpty();
assert !explanation.isEmpty();
mId = id;
mBriefDescription = shortDescription;
mExplanation = explanation;
mCategory = category;
mPriority = priority;
mSeverity = severity;
mImplementation = implementation;
}
/**
* Creates a new issue. The description strings can use some simple markup;
* see the {@link TextFormat#RAW} documentation
* for details.
*
* @param id the fixed id of the issue
* @param briefDescription short summary (typically 5-6 words or less), typically
* describing the <b>problem</b> rather than the <b>fix</b>
* (e.g. "Missing minSdkVersion")
* @param explanation a full explanation of the issue, with suggestions for
* how to fix it
* @param category the associated category, if any
* @param priority the priority, a number from 1 to 10 with 10 being most
* important/severe
* @param severity the default severity of the issue
* @param implementation the default implementation for this issue
* @return a new {@link Issue}
*/
@NonNull
public static Issue create(
@NonNull String id,
@NonNull String briefDescription,
@NonNull String explanation,
@NonNull Category category,
int priority,
@NonNull Severity severity,
@NonNull Implementation implementation) {
return new Issue(id, briefDescription, explanation, category, priority,
severity, implementation);
}
/**
* For compatibility with older custom rules)
*
* @deprecated Use {@link #create(String, String, String, Category, int, Severity, Implementation)} instead
*/
@NonNull
@Deprecated
public static Issue create(
@NonNull String id,
@NonNull String briefDescription,
@SuppressWarnings("UnusedParameters") @NonNull String description,
@NonNull String explanation,
@NonNull Category category,
int priority,
@NonNull Severity severity,
@NonNull Implementation implementation) {
return new Issue(id, briefDescription, explanation, category, priority,
severity, implementation);
}
/**
* Returns the unique id of this issue. These should not change over time
* since they are used to persist the names of issues suppressed by the user
* etc. It is typically a single camel-cased word.
*
* @return the associated fixed id, never null and always unique
*/
@NonNull
public String getId() {
return mId;
}
/**
* Briefly (in a couple of words) describes these errors
*
* @return a brief summary of the issue, never null, never empty
*/
@NonNull
public String getBriefDescription(@NonNull TextFormat format) {
return RAW.convertTo(mBriefDescription, format);
}
/**
* Describes the error found by this rule, e.g.
* "Buttons must define contentDescriptions". Preferably the explanation
* should also contain a description of how the problem should be solved.
* Additional info can be provided via {@link #getMoreInfo()}.
*
* @param format the format to write the format as
* @return an explanation of the issue, never null, never empty
*/
@NonNull
public String getExplanation(@NonNull TextFormat format) {
return RAW.convertTo(mExplanation, format);
}
/**
* The primary category of the issue
*
* @return the primary category of the issue, never null
*/
@NonNull
public Category getCategory() {
return mCategory;
}
/**
* Returns a priority, in the range 1-10, with 10 being the most severe and
* 1 the least
*
* @return a priority from 1 to 10
*/
public int getPriority() {
return mPriority;
}
/**
* Returns the default severity of the issues found by this detector (some
* tools may allow the user to specify custom severities for detectors).
* <p>
* Note that even though the normal way for an issue to be disabled is for
* the {@link Configuration} to return {@link Severity#IGNORE}, there is a
* {@link #isEnabledByDefault()} method which can be used to turn off issues
* by default. This is done rather than just having the severity as the only
* attribute on the issue such that an issue can be configured with an
* appropriate severity (such as {@link Severity#ERROR}) even when issues
* are disabled by default for example because they are experimental or not
* yet stable.
*
* @return the severity of the issues found by this detector
*/
@NonNull
public Severity getDefaultSeverity() {
return mSeverity;
}
/**
* Returns a link (a URL string) to more information, or null
*
* @return a link to more information, or null
*/
@NonNull
public List<String> getMoreInfo() {
if (mMoreInfoUrls == null) {
return Collections.emptyList();
} else if (mMoreInfoUrls instanceof String) {
return Collections.singletonList((String) mMoreInfoUrls);
} else {
assert mMoreInfoUrls instanceof List;
//noinspection unchecked
return (List<String>) mMoreInfoUrls;
}
}
/**
* Adds a more info URL string
*
* @param moreInfoUrl url string
* @return this, for constructor chaining
*/
@NonNull
public Issue addMoreInfo(@NonNull String moreInfoUrl) {
// Nearly all issues supply at most a single URL, so don't bother with
// lists wrappers for most of these issues
if (mMoreInfoUrls == null) {
mMoreInfoUrls = moreInfoUrl;
} else if (mMoreInfoUrls instanceof String) {
String existing = (String) mMoreInfoUrls;
List<String> list = new ArrayList<String>(2);
list.add(existing);
list.add(moreInfoUrl);
mMoreInfoUrls = list;
} else {
assert mMoreInfoUrls instanceof List;
//noinspection unchecked
((List<String>) mMoreInfoUrls).add(moreInfoUrl);
}
return this;
}
/**
* Returns whether this issue should be enabled by default, unless the user
* has explicitly disabled it.
*
* @return true if this issue should be enabled by default
*/
public boolean isEnabledByDefault() {
return mEnabledByDefault;
}
/**
* Returns the implementation for the given issue
*
* @return the implementation for this issue
*/
@NonNull
public Implementation getImplementation() {
return mImplementation;
}
/**
* Sets the implementation for the given issue. This is typically done by
* IDEs that can offer a replacement for a given issue which performs better
* or in some other way works better within the IDE.
*
* @param implementation the new implementation to use
*/
public void setImplementation(@NonNull Implementation implementation) {
mImplementation = implementation;
}
/**
* Sorts the detectors alphabetically by id. This is intended to make it
* convenient to store settings for detectors in a fixed order. It is not
* intended as the order to be shown to the user; for that, a tool embedding
* lint might consider the priorities, categories, severities etc of the
* various detectors.
*
* @param other the {@link Issue} to compare this issue to
*/
@Override
public int compareTo(@NonNull Issue other) {
return getId().compareTo(other.getId());
}
/**
* Sets whether this issue is enabled by default.
*
* @param enabledByDefault whether the issue should be enabled by default
* @return this, for constructor chaining
*/
@NonNull
public Issue setEnabledByDefault(boolean enabledByDefault) {
mEnabledByDefault = enabledByDefault;
return this;
}
@Override
public String toString() {
return mId;
}
}