blob: c059fd69a7f2289130c29fa0f0b5215a945af8b9 [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.checks;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import org.w3c.dom.Element;
import java.util.Collection;
/**
* Checks whether a view hierarchy has too many views or has a suspiciously deep hierarchy
*/
public class TooManyViewsDetector extends LayoutDetector {
private static final Implementation IMPLEMENTATION = new Implementation(
TooManyViewsDetector.class,
Scope.RESOURCE_FILE_SCOPE);
/** Issue of having too many views in a single layout */
public static final Issue TOO_MANY = Issue.create(
"TooManyViews", //$NON-NLS-1$
"Layout has too many views",
"Using too many views in a single layout is bad for " +
"performance. Consider using compound drawables or other tricks for " +
"reducing the number of views in this layout.\n" +
"\n" +
"The maximum view count defaults to 80 but can be configured with the " +
"environment variable `ANDROID_LINT_MAX_VIEW_COUNT`.",
Category.PERFORMANCE,
1,
Severity.WARNING,
IMPLEMENTATION);
/** Issue of having too deep hierarchies in layouts */
public static final Issue TOO_DEEP = Issue.create(
"TooDeepLayout", //$NON-NLS-1$
"Layout hierarchy is too deep",
"Layouts with too much nesting is bad for performance. " +
"Consider using a flatter layout (such as `RelativeLayout` or `GridLayout`)." +
"The default maximum depth is 10 but can be configured with the environment " +
"variable `ANDROID_LINT_MAX_DEPTH`.",
Category.PERFORMANCE,
1,
Severity.WARNING,
IMPLEMENTATION);
private static final int MAX_VIEW_COUNT;
private static final int MAX_DEPTH;
static {
int maxViewCount = 0;
int maxDepth = 0;
String countValue = System.getenv("ANDROID_LINT_MAX_VIEW_COUNT"); //$NON-NLS-1$
if (countValue != null) {
try {
maxViewCount = Integer.parseInt(countValue);
} catch (NumberFormatException e) {
// pass: set to default below
}
}
String depthValue = System.getenv("ANDROID_LINT_MAX_DEPTH"); //$NON-NLS-1$
if (depthValue != null) {
try {
maxDepth = Integer.parseInt(depthValue);
} catch (NumberFormatException e) {
// pass: set to default below
}
}
if (maxViewCount == 0) {
maxViewCount = 80;
}
if (maxDepth == 0) {
maxDepth = 10;
}
MAX_VIEW_COUNT = maxViewCount;
MAX_DEPTH = maxDepth;
}
private int mViewCount;
private int mDepth;
private boolean mWarnedAboutDepth;
/** Constructs a new {@link TooManyViewsDetector} */
public TooManyViewsDetector() {
}
@NonNull
@Override
public Speed getSpeed() {
return Speed.FAST;
}
@Override
public void beforeCheckFile(@NonNull Context context) {
mViewCount = mDepth = 0;
mWarnedAboutDepth = false;
}
@Override
public Collection<String> getApplicableElements() {
return ALL;
}
@Override
public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
mViewCount++;
mDepth++;
if (mDepth == MAX_DEPTH && !mWarnedAboutDepth) {
// Have to record whether or not we've warned since we could have many siblings
// at the max level and we'd warn for each one. No need to do the same thing
// for the view count error since we'll only have view count exactly equal the
// max just once.
mWarnedAboutDepth = true;
String msg = String.format("`%1$s` has more than %2$d levels, bad for performance",
context.file.getName(), MAX_DEPTH);
context.report(TOO_DEEP, element, context.getLocation(element), msg);
}
if (mViewCount == MAX_VIEW_COUNT) {
String msg = String.format("`%1$s` has more than %2$d views, bad for performance",
context.file.getName(), MAX_VIEW_COUNT);
context.report(TOO_MANY, element, context.getLocation(element), msg);
}
}
@Override
public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) {
mDepth--;
}
}