com.android.tools.lint.client.api.DefaultSdkInfo Maven / Gradle / Ivy
/*
* 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.client.api;
import static com.android.SdkConstants.ABSOLUTE_LAYOUT;
import static com.android.SdkConstants.ABS_LIST_VIEW;
import static com.android.SdkConstants.ABS_SEEK_BAR;
import static com.android.SdkConstants.ABS_SPINNER;
import static com.android.SdkConstants.ADAPTER_VIEW;
import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
import static com.android.SdkConstants.BUTTON;
import static com.android.SdkConstants.CHECKABLE;
import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
import static com.android.SdkConstants.CHECK_BOX;
import static com.android.SdkConstants.COMPOUND_BUTTON;
import static com.android.SdkConstants.EDIT_TEXT;
import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
import static com.android.SdkConstants.FRAME_LAYOUT;
import static com.android.SdkConstants.GALLERY;
import static com.android.SdkConstants.GRID_VIEW;
import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW;
import static com.android.SdkConstants.IMAGE_BUTTON;
import static com.android.SdkConstants.IMAGE_VIEW;
import static com.android.SdkConstants.LINEAR_LAYOUT;
import static com.android.SdkConstants.LIST_VIEW;
import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
import static com.android.SdkConstants.PROGRESS_BAR;
import static com.android.SdkConstants.RADIO_BUTTON;
import static com.android.SdkConstants.RADIO_GROUP;
import static com.android.SdkConstants.RELATIVE_LAYOUT;
import static com.android.SdkConstants.SCROLL_VIEW;
import static com.android.SdkConstants.SEEK_BAR;
import static com.android.SdkConstants.SPINNER;
import static com.android.SdkConstants.SURFACE_VIEW;
import static com.android.SdkConstants.SWITCH;
import static com.android.SdkConstants.TABLE_LAYOUT;
import static com.android.SdkConstants.TABLE_ROW;
import static com.android.SdkConstants.TAB_HOST;
import static com.android.SdkConstants.TAB_WIDGET;
import static com.android.SdkConstants.TEXT_VIEW;
import static com.android.SdkConstants.TOGGLE_BUTTON;
import static com.android.SdkConstants.VIEW;
import static com.android.SdkConstants.VIEW_ANIMATOR;
import static com.android.SdkConstants.VIEW_GROUP;
import static com.android.SdkConstants.VIEW_PKG_PREFIX;
import static com.android.SdkConstants.VIEW_STUB;
import static com.android.SdkConstants.VIEW_SWITCHER;
import static com.android.SdkConstants.WEB_VIEW;
import static com.android.SdkConstants.WIDGET_PKG_PREFIX;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Default simple implementation of an {@link SdkInfo}
*
* 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.
*/
@Beta
class DefaultSdkInfo extends SdkInfo {
@Override
@Nullable
public String getParentViewName(@NonNull String name) {
name = getRawType(name);
return PARENTS.get(name);
}
@Override
@Nullable
public String getParentViewClass(@NonNull String fqcn) {
int index = fqcn.lastIndexOf('.');
if (index != -1) {
fqcn = fqcn.substring(index + 1);
}
String parent = PARENTS.get(fqcn);
if (parent == null) {
return null;
}
// The map only stores class names internally; correct for full package paths
if (parent.equals(VIEW) || parent.equals(VIEW_GROUP) || parent.equals(SURFACE_VIEW)) {
return VIEW_PKG_PREFIX + parent;
} else {
return WIDGET_PKG_PREFIX + parent;
}
}
@Override
public boolean isSubViewOf(@NonNull String parentType, @NonNull String childType) {
String parent = getRawType(parentType);
String child = getRawType(childType);
// Do analysis just on non-fqcn paths
if (parent.indexOf('.') != -1) {
parent = parent.substring(parent.lastIndexOf('.') + 1);
}
if (child.indexOf('.') != -1) {
child = child.substring(child.lastIndexOf('.') + 1);
}
if (parent.equals(VIEW)) {
return true;
}
while (!child.equals(VIEW)) {
if (parent.equals(child)) {
return true;
}
if (implementsInterface(child, parentType)) {
return true;
}
child = PARENTS.get(child);
if (child == null) {
// Unknown view - err on the side of caution
return true;
}
}
return false;
}
private static boolean implementsInterface(String className, String interfaceName) {
return interfaceName.equals(INTERFACES.get(className));
}
// Strip off type parameters, e.g. AdapterView> => AdapterView
private static String getRawType(String type) {
if (type != null) {
int index = type.indexOf('<');
if (index != -1) {
type = type.substring(0, index);
}
}
return type;
}
@Override
public boolean isLayout(@NonNull String tag) {
// TODO: Read in widgets.txt from the platform install area to look up this information
// dynamically instead!
if (super.isLayout(tag)) {
return true;
}
return LAYOUTS.contains(tag);
}
private static final int CLASS_COUNT = 59;
private static final int LAYOUT_COUNT = 20;
private static final Map PARENTS = Maps.newHashMapWithExpectedSize(CLASS_COUNT);
private static final Set LAYOUTS = Sets.newHashSetWithExpectedSize(CLASS_COUNT);
static {
PARENTS.put(COMPOUND_BUTTON, BUTTON);
PARENTS.put(ABS_SPINNER, ADAPTER_VIEW);
PARENTS.put(ABS_LIST_VIEW, ADAPTER_VIEW);
PARENTS.put(ABS_SEEK_BAR, ADAPTER_VIEW);
PARENTS.put(ADAPTER_VIEW, VIEW_GROUP);
PARENTS.put(VIEW_GROUP, VIEW);
PARENTS.put(TEXT_VIEW, VIEW);
PARENTS.put(CHECKED_TEXT_VIEW, TEXT_VIEW);
PARENTS.put(RADIO_BUTTON, COMPOUND_BUTTON);
PARENTS.put(SPINNER, ABS_SPINNER);
PARENTS.put(IMAGE_BUTTON, IMAGE_VIEW);
PARENTS.put(IMAGE_VIEW, VIEW);
PARENTS.put(EDIT_TEXT, TEXT_VIEW);
PARENTS.put(PROGRESS_BAR, VIEW);
PARENTS.put(TOGGLE_BUTTON, COMPOUND_BUTTON);
PARENTS.put(VIEW_STUB, VIEW);
PARENTS.put(BUTTON, TEXT_VIEW);
PARENTS.put(SEEK_BAR, ABS_SEEK_BAR);
PARENTS.put(CHECK_BOX, COMPOUND_BUTTON);
PARENTS.put(SWITCH, COMPOUND_BUTTON);
PARENTS.put(GALLERY, ABS_SPINNER);
PARENTS.put(SURFACE_VIEW, VIEW);
PARENTS.put(ABSOLUTE_LAYOUT, VIEW_GROUP);
PARENTS.put(LINEAR_LAYOUT, VIEW_GROUP);
PARENTS.put(RELATIVE_LAYOUT, VIEW_GROUP);
PARENTS.put(LIST_VIEW, ABS_LIST_VIEW);
PARENTS.put(VIEW_SWITCHER, VIEW_ANIMATOR);
PARENTS.put(FRAME_LAYOUT, VIEW_GROUP);
PARENTS.put(HORIZONTAL_SCROLL_VIEW, FRAME_LAYOUT);
PARENTS.put(VIEW_ANIMATOR, FRAME_LAYOUT);
PARENTS.put(TAB_HOST, FRAME_LAYOUT);
PARENTS.put(TABLE_ROW, LINEAR_LAYOUT);
PARENTS.put(RADIO_GROUP, LINEAR_LAYOUT);
PARENTS.put(TAB_WIDGET, LINEAR_LAYOUT);
PARENTS.put(EXPANDABLE_LIST_VIEW, LIST_VIEW);
PARENTS.put(TABLE_LAYOUT, LINEAR_LAYOUT);
PARENTS.put(SCROLL_VIEW, FRAME_LAYOUT);
PARENTS.put(GRID_VIEW, ABS_LIST_VIEW);
PARENTS.put(WEB_VIEW, ABSOLUTE_LAYOUT);
PARENTS.put(AUTO_COMPLETE_TEXT_VIEW, EDIT_TEXT);
PARENTS.put(MULTI_AUTO_COMPLETE_TEXT_VIEW, AUTO_COMPLETE_TEXT_VIEW);
PARENTS.put(CHECKED_TEXT_VIEW, TEXT_VIEW);
PARENTS.put("MediaController", FRAME_LAYOUT); //$NON-NLS-1$
PARENTS.put("SlidingDrawer", VIEW_GROUP); //$NON-NLS-1$
PARENTS.put("DialerFilter", RELATIVE_LAYOUT); //$NON-NLS-1$
PARENTS.put("DigitalClock", TEXT_VIEW); //$NON-NLS-1$
PARENTS.put("Chronometer", TEXT_VIEW); //$NON-NLS-1$
PARENTS.put("ImageSwitcher", VIEW_SWITCHER); //$NON-NLS-1$
PARENTS.put("TextSwitcher", VIEW_SWITCHER); //$NON-NLS-1$
PARENTS.put("AnalogClock", VIEW); //$NON-NLS-1$
PARENTS.put("TwoLineListItem", RELATIVE_LAYOUT); //$NON-NLS-1$
PARENTS.put("ZoomControls", LINEAR_LAYOUT); //$NON-NLS-1$
PARENTS.put("DatePicker", FRAME_LAYOUT); //$NON-NLS-1$
PARENTS.put("TimePicker", FRAME_LAYOUT); //$NON-NLS-1$
PARENTS.put("VideoView", SURFACE_VIEW); //$NON-NLS-1$
PARENTS.put("ZoomButton", IMAGE_BUTTON); //$NON-NLS-1$
PARENTS.put("RatingBar", ABS_SEEK_BAR); //$NON-NLS-1$
PARENTS.put("ViewFlipper", VIEW_ANIMATOR); //$NON-NLS-1$
PARENTS.put("NumberPicker", LINEAR_LAYOUT); //$NON-NLS-1$
assert PARENTS.size() <= CLASS_COUNT : PARENTS.size();
/*
// Check that all widgets lead to the root view
if (LintUtils.assertionsEnabled()) {
for (String key : PARENTS.keySet()) {
String parent = PARENTS.get(key);
if (!parent.equals(VIEW)) {
String grandParent = PARENTS.get(parent);
assert grandParent != null : parent;
}
}
}
*/
LAYOUTS.add(TAB_HOST);
LAYOUTS.add(HORIZONTAL_SCROLL_VIEW);
LAYOUTS.add(VIEW_SWITCHER);
LAYOUTS.add(TAB_WIDGET);
LAYOUTS.add(VIEW_ANIMATOR);
LAYOUTS.add(SCROLL_VIEW);
LAYOUTS.add(GRID_VIEW);
LAYOUTS.add(TABLE_ROW);
LAYOUTS.add(RADIO_GROUP);
LAYOUTS.add(LIST_VIEW);
LAYOUTS.add(EXPANDABLE_LIST_VIEW);
LAYOUTS.add("MediaController"); //$NON-NLS-1$
LAYOUTS.add("DialerFilter"); //$NON-NLS-1$
LAYOUTS.add("ViewFlipper"); //$NON-NLS-1$
LAYOUTS.add("SlidingDrawer"); //$NON-NLS-1$
LAYOUTS.add("StackView"); //$NON-NLS-1$
LAYOUTS.add("SearchView"); //$NON-NLS-1$
LAYOUTS.add("TextSwitcher"); //$NON-NLS-1$
LAYOUTS.add("AdapterViewFlipper"); //$NON-NLS-1$
LAYOUTS.add("ImageSwitcher"); //$NON-NLS-1$
assert LAYOUTS.size() <= LAYOUT_COUNT : LAYOUTS.size();
}
// Currently using a map; this should really be a list, but using a map until we actually
// start adding more than one item
@NonNull
private static final Map INTERFACES = new HashMap(2);
static {
INTERFACES.put(CHECKED_TEXT_VIEW, CHECKABLE);
INTERFACES.put(COMPOUND_BUTTON, CHECKABLE);
}
}