blob: cb765570aa74b3214a05d3a16c9eb2930fbcd97b [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.XmlContext;
import java.util.Collection;
import org.w3c.dom.Element;
/** 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",
"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",
"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");
if (countValue != null) {
try {
maxViewCount = Integer.parseInt(countValue);
} catch (NumberFormatException e) {
// pass: set to default below
}
}
String depthValue = System.getenv("ANDROID_LINT_MAX_DEPTH");
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() {}
@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.getElementLocation(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.getElementLocation(element), msg);
}
}
@Override
public void visitElementAfter(@NonNull XmlContext context, @NonNull Element element) {
mDepth--;
}
}