1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.lint.client.api; 18 19 import static com.android.tools.lint.detector.api.LintConstants.ABSOLUTE_LAYOUT; 20 import static com.android.tools.lint.detector.api.LintConstants.ABS_LIST_VIEW; 21 import static com.android.tools.lint.detector.api.LintConstants.ABS_SEEK_BAR; 22 import static com.android.tools.lint.detector.api.LintConstants.ABS_SPINNER; 23 import static com.android.tools.lint.detector.api.LintConstants.ADAPTER_VIEW; 24 import static com.android.tools.lint.detector.api.LintConstants.BUTTON; 25 import static com.android.tools.lint.detector.api.LintConstants.CHECKED_TEXT_VIEW; 26 import static com.android.tools.lint.detector.api.LintConstants.CHECK_BOX; 27 import static com.android.tools.lint.detector.api.LintConstants.COMPOUND_BUTTON; 28 import static com.android.tools.lint.detector.api.LintConstants.EDIT_TEXT; 29 import static com.android.tools.lint.detector.api.LintConstants.EXPANDABLE_LIST_VIEW; 30 import static com.android.tools.lint.detector.api.LintConstants.FRAME_LAYOUT; 31 import static com.android.tools.lint.detector.api.LintConstants.GALLERY; 32 import static com.android.tools.lint.detector.api.LintConstants.GRID_VIEW; 33 import static com.android.tools.lint.detector.api.LintConstants.HORIZONTAL_SCROLL_VIEW; 34 import static com.android.tools.lint.detector.api.LintConstants.IMAGE_BUTTON; 35 import static com.android.tools.lint.detector.api.LintConstants.IMAGE_VIEW; 36 import static com.android.tools.lint.detector.api.LintConstants.LINEAR_LAYOUT; 37 import static com.android.tools.lint.detector.api.LintConstants.LIST_VIEW; 38 import static com.android.tools.lint.detector.api.LintConstants.PROGRESS_BAR; 39 import static com.android.tools.lint.detector.api.LintConstants.RADIO_BUTTON; 40 import static com.android.tools.lint.detector.api.LintConstants.RADIO_GROUP; 41 import static com.android.tools.lint.detector.api.LintConstants.RELATIVE_LAYOUT; 42 import static com.android.tools.lint.detector.api.LintConstants.SCROLL_VIEW; 43 import static com.android.tools.lint.detector.api.LintConstants.SEEK_BAR; 44 import static com.android.tools.lint.detector.api.LintConstants.SPINNER; 45 import static com.android.tools.lint.detector.api.LintConstants.SURFACE_VIEW; 46 import static com.android.tools.lint.detector.api.LintConstants.SWITCH; 47 import static com.android.tools.lint.detector.api.LintConstants.TABLE_LAYOUT; 48 import static com.android.tools.lint.detector.api.LintConstants.TABLE_ROW; 49 import static com.android.tools.lint.detector.api.LintConstants.TAB_HOST; 50 import static com.android.tools.lint.detector.api.LintConstants.TAB_WIDGET; 51 import static com.android.tools.lint.detector.api.LintConstants.TEXT_VIEW; 52 import static com.android.tools.lint.detector.api.LintConstants.TOGGLE_BUTTON; 53 import static com.android.tools.lint.detector.api.LintConstants.VIEW; 54 import static com.android.tools.lint.detector.api.LintConstants.VIEW_ANIMATOR; 55 import static com.android.tools.lint.detector.api.LintConstants.VIEW_GROUP; 56 import static com.android.tools.lint.detector.api.LintConstants.VIEW_PKG_PREFIX; 57 import static com.android.tools.lint.detector.api.LintConstants.VIEW_STUB; 58 import static com.android.tools.lint.detector.api.LintConstants.VIEW_SWITCHER; 59 import static com.android.tools.lint.detector.api.LintConstants.WEB_VIEW; 60 import static com.android.tools.lint.detector.api.LintConstants.WIDGET_PKG_PREFIX; 61 62 import com.android.annotations.NonNull; 63 import com.android.annotations.Nullable; 64 import com.google.common.annotations.Beta; 65 66 import java.util.HashMap; 67 import java.util.Map; 68 69 /** 70 * Default simple implementation of an {@link SdkInfo} 71 * <p> 72 * <b>NOTE: This is not a public or final API; if you rely on this be prepared 73 * to adjust your code for the next tools release.</b> 74 */ 75 @Beta 76 class DefaultSdkInfo extends SdkInfo { 77 @Override 78 @Nullable 79 public String getParentViewName(@NonNull String name) { 80 name = getRawType(name); 81 82 return PARENTS.get(name); 83 } 84 85 @Override 86 @Nullable 87 public String getParentViewClass(@NonNull String fqcn) { 88 int index = fqcn.lastIndexOf('.'); 89 if (index != -1) { 90 fqcn = fqcn.substring(index + 1); 91 } 92 93 String parent = PARENTS.get(fqcn); 94 if (parent == null) { 95 return null; 96 } 97 // The map only stores class names internally; correct for full package paths 98 if (parent.equals(VIEW) || parent.equals(VIEW_GROUP) || parent.equals(SURFACE_VIEW)) { 99 return VIEW_PKG_PREFIX + parent; 100 } else { 101 return WIDGET_PKG_PREFIX + parent; 102 } 103 } 104 105 @Override 106 public boolean isSubViewOf(@NonNull String parent, @NonNull String child) { 107 parent = getRawType(parent); 108 child = getRawType(child); 109 110 // Do analysis just on non-fqcn paths 111 if (parent.indexOf('.') != -1) { 112 parent = parent.substring(parent.lastIndexOf('.') + 1); 113 } 114 if (child.indexOf('.') != -1) { 115 child = child.substring(child.lastIndexOf('.') + 1); 116 } 117 118 if (parent.equals(VIEW)) { 119 return true; 120 } 121 122 while (!child.equals(VIEW)) { 123 if (parent.equals(child)) { 124 return true; 125 } 126 child = PARENTS.get(child); 127 if (child == null) { 128 // Unknown view - err on the side of caution 129 return true; 130 } 131 } 132 133 return false; 134 } 135 136 // Strip off type parameters, e.g. AdapterView<?> => AdapterView 137 private static String getRawType(String type) { 138 if (type != null) { 139 int index = type.indexOf('<'); 140 if (index != -1) { 141 type = type.substring(0, index); 142 } 143 } 144 145 return type; 146 } 147 148 private static final int CLASS_COUNT = 59; 149 150 @NonNull 151 private static final Map<String, String> PARENTS = new HashMap<String, String>(CLASS_COUNT); 152 153 static { 154 PARENTS.put(COMPOUND_BUTTON, BUTTON); 155 PARENTS.put(ABS_SPINNER, ADAPTER_VIEW); 156 PARENTS.put(ABS_LIST_VIEW, ADAPTER_VIEW); 157 PARENTS.put(ABS_SEEK_BAR, ADAPTER_VIEW); 158 PARENTS.put(ADAPTER_VIEW, VIEW_GROUP); 159 PARENTS.put(VIEW_GROUP, VIEW); 160 161 PARENTS.put(TEXT_VIEW, VIEW); 162 PARENTS.put(CHECKED_TEXT_VIEW, TEXT_VIEW); 163 PARENTS.put(RADIO_BUTTON, COMPOUND_BUTTON); 164 PARENTS.put(SPINNER, ABS_SPINNER); 165 PARENTS.put(IMAGE_BUTTON, IMAGE_VIEW); 166 PARENTS.put(IMAGE_VIEW, VIEW); 167 PARENTS.put(EDIT_TEXT, TEXT_VIEW); 168 PARENTS.put(PROGRESS_BAR, VIEW); 169 PARENTS.put(TOGGLE_BUTTON, COMPOUND_BUTTON); 170 PARENTS.put(VIEW_STUB, VIEW); 171 PARENTS.put(BUTTON, TEXT_VIEW); 172 PARENTS.put(SEEK_BAR, ABS_SEEK_BAR); 173 PARENTS.put(CHECK_BOX, COMPOUND_BUTTON); 174 PARENTS.put(SWITCH, COMPOUND_BUTTON); 175 PARENTS.put(GALLERY, ABS_SPINNER); 176 PARENTS.put(SURFACE_VIEW, VIEW); 177 PARENTS.put(ABSOLUTE_LAYOUT, VIEW_GROUP); 178 PARENTS.put(LINEAR_LAYOUT, VIEW_GROUP); 179 PARENTS.put(RELATIVE_LAYOUT, VIEW_GROUP); 180 PARENTS.put(LIST_VIEW, ABS_LIST_VIEW); 181 PARENTS.put(VIEW_SWITCHER, VIEW_ANIMATOR); 182 PARENTS.put(FRAME_LAYOUT, VIEW_GROUP); 183 PARENTS.put(HORIZONTAL_SCROLL_VIEW, FRAME_LAYOUT); 184 PARENTS.put(VIEW_ANIMATOR, FRAME_LAYOUT); 185 PARENTS.put(TAB_HOST, FRAME_LAYOUT); 186 PARENTS.put(TABLE_ROW, LINEAR_LAYOUT); 187 PARENTS.put(RADIO_GROUP, LINEAR_LAYOUT); 188 PARENTS.put(TAB_WIDGET, LINEAR_LAYOUT); 189 PARENTS.put(EXPANDABLE_LIST_VIEW, LIST_VIEW); 190 PARENTS.put(TABLE_LAYOUT, LINEAR_LAYOUT); 191 PARENTS.put(SCROLL_VIEW, FRAME_LAYOUT); 192 PARENTS.put(GRID_VIEW, ABS_LIST_VIEW); 193 PARENTS.put(WEB_VIEW, ABSOLUTE_LAYOUT); 194 195 PARENTS.put("CheckedTextView", TEXT_VIEW); //$NON-NLS-1$ 196 PARENTS.put("MediaController", FRAME_LAYOUT); //$NON-NLS-1$ 197 PARENTS.put("SlidingDrawer", VIEW_GROUP); //$NON-NLS-1$ 198 PARENTS.put("DialerFilter", RELATIVE_LAYOUT); //$NON-NLS-1$ 199 PARENTS.put("DigitalClock", TEXT_VIEW); //$NON-NLS-1$ 200 PARENTS.put("Chronometer", TEXT_VIEW); //$NON-NLS-1$ 201 PARENTS.put("ImageSwitcher", VIEW_SWITCHER); //$NON-NLS-1$ 202 PARENTS.put("TextSwitcher", VIEW_SWITCHER); //$NON-NLS-1$ 203 PARENTS.put("AnalogClock", VIEW); //$NON-NLS-1$ 204 PARENTS.put("TwoLineListItem", RELATIVE_LAYOUT); //$NON-NLS-1$ 205 PARENTS.put("ZoomControls", LINEAR_LAYOUT); //$NON-NLS-1$ 206 PARENTS.put("DatePicker", FRAME_LAYOUT); //$NON-NLS-1$ 207 PARENTS.put("TimePicker", FRAME_LAYOUT); //$NON-NLS-1$ 208 PARENTS.put("VideoView", SURFACE_VIEW); //$NON-NLS-1$ 209 PARENTS.put("ZoomButton", IMAGE_BUTTON); //$NON-NLS-1$ 210 PARENTS.put("AutoCompleteTextView", EDIT_TEXT); //$NON-NLS-1$ 211 PARENTS.put("RatingBar", ABS_SEEK_BAR); //$NON-NLS-1$ 212 PARENTS.put("ViewFlipper", VIEW_ANIMATOR); //$NON-NLS-1$ 213 PARENTS.put("NumberPicker", LINEAR_LAYOUT); //$NON-NLS-1$ 214 PARENTS.put("MultiAutoCompleteTextView", //$NON-NLS-1$ 215 "AutoCompleteTextView"); //$NON-NLS-1$ 216 217 assert PARENTS.size() <= CLASS_COUNT : PARENTS.size(); 218 219 /* 220 // Check that all widgets lead to the root view 221 if (LintUtils.assertionsEnabled()) { 222 for (String key : PARENTS.keySet()) { 223 String parent = PARENTS.get(key); 224 if (!parent.equals(VIEW)) { 225 String grandParent = PARENTS.get(parent); 226 assert grandParent != null : parent; 227 } 228 } 229 } 230 */ 231 } 232 } 233