Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2012 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 static com.android.SdkConstants.CONSTRUCTOR_NAME;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.ClassScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
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.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Checks for accidental overrides
*/
public class OverrideDetector extends Detector implements ClassScanner {
/** Accidental overrides */
public static final Issue ISSUE = Issue.create(
"DalvikOverride", //$NON-NLS-1$
"Method considered overridden by Dalvik",
"The Android virtual machine will treat a package private method in one " +
"class as overriding a package private method in its super class, even if " +
"they are in separate packages. This may be surprising, but for compatibility " +
"reasons the behavior has not been changed (yet).\n" +
"\n" +
"If you really did intend for this method to override the other, make the " +
"method `protected` instead.\n" +
"\n" +
"If you did *not* intend the override, consider making the method private, or " +
"changing its name or signature.",
Category.CORRECTNESS,
7,
Severity.ERROR,
new Implementation(
OverrideDetector.class,
EnumSet.of(Scope.ALL_CLASS_FILES)));
/** map from owner class name to JVM signatures for its package private methods */
private final Map> mPackagePrivateMethods = Maps.newHashMap();
/** Map from owner to signature to super class being overridden */
private Map> mErrors;
/**
* Map from owner to signature to corresponding location. When there are
* errors a single error can have locations for both the overriding and
* overridden methods.
*/
private Map> mLocations;
/** Constructs a new {@link OverrideDetector} */
public OverrideDetector() {
}
@NonNull
@Override
public Speed getSpeed() {
return Speed.NORMAL;
}
@Override
public void afterCheckProject(@NonNull Context context) {
// Process the check in two passes:
//
// In the first pass, gather the full set of package private methods for
// each class.
// When all classes have been processed at the end of the first pass,
// find out whether any of the methods are potentially overriding those
// in its super classes.
//
// If so, request a second pass. In the second pass, we gather full locations
// for both the base and overridden method calls, and store these.
// If the location is found to be in a suppressed context, remove that error
// entry.
//
// At the end of the second pass, we generate the errors, combining locations
// from both the overridden and overriding methods.
if (context.getPhase() == 1) {
Set classes = mPackagePrivateMethods.keySet();
LintDriver driver = context.getDriver();
for (String owner : classes) {
Set methods = mPackagePrivateMethods.get(owner);
String superClass = driver.getSuperClass(owner);
int packageIndex = owner.lastIndexOf('/');
while (superClass != null) {
int superPackageIndex = superClass.lastIndexOf('/');
// Only compare methods that differ in packages
if (packageIndex == -1 || superPackageIndex != packageIndex ||
!owner.regionMatches(0, superClass, 0, packageIndex)) {
Set superMethods = mPackagePrivateMethods.get(superClass);
if (superMethods != null) {
SetView intersection = Sets.intersection(methods,
superMethods);
if (!intersection.isEmpty()) {
if (mLocations == null) {
mLocations = Maps.newHashMap();
}
// We need a separate data structure to keep track of which
// signatures are in error,
if (mErrors == null) {
mErrors = Maps.newHashMap();
}
for (String signature : intersection) {
Map locations = mLocations.get(owner);
if (locations == null) {
locations = Maps.newHashMap();
mLocations.put(owner, locations);
}
locations.put(signature, null);
locations = mLocations.get(superClass);
if (locations == null) {
locations = Maps.newHashMap();
mLocations.put(superClass, locations);
}
locations.put(signature, null);
Map errors = mErrors.get(owner);
if (errors == null) {
errors = Maps.newHashMap();
mErrors.put(owner, errors);
}
errors.put(signature, superClass);
}
}
}
}
superClass = driver.getSuperClass(superClass);
}
}
if (mErrors != null) {
context.requestRepeat(this, ISSUE.getImplementation().getScope());
}
} else {
assert context.getPhase() == 2;
for (Entry> ownerEntry : mErrors.entrySet()) {
String owner = ownerEntry.getKey();
Map methodToSuper = ownerEntry.getValue();
for (Entry entry : methodToSuper.entrySet()) {
String signature = entry.getKey();
String superClass = entry.getValue();
Map ownerLocations = mLocations.get(owner);
if (ownerLocations != null) {
Location location = ownerLocations.get(signature);
if (location != null) {
Map superLocations = mLocations.get(superClass);
if (superLocations != null) {
Location superLocation = superLocations.get(signature);
if (superLocation != null) {
location.setSecondary(superLocation);
superLocation.setMessage(
"This method is treated as overridden");
}
}
String methodName = signature;
int index = methodName.indexOf('(');
if (index != -1) {
methodName = methodName.substring(0, index);
}
String message = String.format(
"This package private method may be unintentionally " +
"overriding `%1$s` in `%2$s`", methodName,
ClassContext.getFqcn(superClass));
context.report(ISSUE, location, message);
}
}
}
}
}
}
@SuppressWarnings("rawtypes") // ASM5 API
@Override
public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) {
if (!context.getProject().getReportIssues()) {
// If this is a library project not being analyzed, ignore it
return;
}
List methodList = classNode.methods;
if (context.getPhase() == 1) {
for (Object m : methodList) {
MethodNode method = (MethodNode) m;
int access = method.access;
// Only record non-static package private methods
if ((access & (ACC_STATIC|ACC_PRIVATE|ACC_PROTECTED|ACC_PUBLIC)) != 0) {
continue;
}
// Ignore constructors too
if (CONSTRUCTOR_NAME.equals(method.name)) {
continue;
}
String owner = classNode.name;
Set methods = mPackagePrivateMethods.get(owner);
if (methods == null) {
methods = Sets.newHashSetWithExpectedSize(methodList.size());
mPackagePrivateMethods.put(owner, methods);
}
methods.add(method.name + method.desc);
}
} else {
assert context.getPhase() == 2;
Map methods = mLocations.get(classNode.name);
if (methods == null) {
// No locations needed from this class
return;
}
for (Object m : methodList) {
MethodNode method = (MethodNode) m;
String signature = method.name + method.desc;
if (methods.containsKey(signature)){
if (context.getDriver().isSuppressed(ISSUE, classNode,
method, null)) {
Map errors = mErrors.get(classNode.name);
if (errors != null) {
errors.remove(signature);
}
continue;
}
Location location = context.getLocation(method, classNode);
methods.put(signature, location);
String description = ClassContext.createSignature(classNode.name,
method.name, method.desc);
location.setClientData(description);
}
}
}
}
}